mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-06-20 19:41:16 +02:00
added support for trusted domains, see #28
This commit is contained in:
@@ -48,6 +48,8 @@ import com4j.typelibs.activeDirectory.IADsGroup;
|
||||
import com4j.typelibs.activeDirectory.IADsOpenDSObject;
|
||||
import com4j.typelibs.activeDirectory.IADsUser;
|
||||
import com4j.typelibs.ado20.ClassFactory;
|
||||
import com4j.typelibs.ado20.Field;
|
||||
import com4j.typelibs.ado20.Fields;
|
||||
import com4j.typelibs.ado20._Command;
|
||||
import com4j.typelibs.ado20._Connection;
|
||||
import com4j.typelibs.ado20._Recordset;
|
||||
@@ -60,6 +62,7 @@ import sonia.scm.plugin.ext.Extension;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.util.AssertUtil;
|
||||
import sonia.scm.util.SystemUtil;
|
||||
import sonia.scm.util.Util;
|
||||
import sonia.scm.web.security.AuthenticationHandler;
|
||||
import sonia.scm.web.security.AuthenticationResult;
|
||||
|
||||
@@ -68,6 +71,8 @@ import sonia.scm.web.security.AuthenticationResult;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -177,6 +182,7 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
con.provider("ADsDSOObject");
|
||||
con.open("Active Directory Provider", "" /* default */, "" /* default */,
|
||||
-1 /* default */);
|
||||
readDomains(rootDSE);
|
||||
logger.debug("Connected to Active Directory");
|
||||
}
|
||||
catch (ExecutionException ex)
|
||||
@@ -203,24 +209,27 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param domainContext
|
||||
* @param userOrGroupname
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getDnOfUserOrGroup(String userOrGroupname)
|
||||
protected String getDnOfUserOrGroup(String domainContext,
|
||||
String userOrGroupname)
|
||||
{
|
||||
String dn;
|
||||
_Command cmd = ClassFactory.createCommand();
|
||||
|
||||
cmd.activeConnection(con);
|
||||
cmd.commandText("<LDAP://" + defaultNamingContext + ">;(sAMAccountName="
|
||||
cmd.commandText("<LDAP://" + domainContext + ">;(sAMAccountName="
|
||||
+ userOrGroupname + ");distinguishedName;subTree");
|
||||
|
||||
_Recordset rs = cmd.execute(null, Variant.MISSING, -1 /* default */);
|
||||
|
||||
if (!rs.eof())
|
||||
{
|
||||
dn = rs.fields().item("distinguishedName").value().toString();
|
||||
dn = getString(rs, "distinguishedName");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -254,7 +263,43 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
}
|
||||
|
||||
AuthenticationResult result;
|
||||
String dn = getDnOfUserOrGroup(username);
|
||||
String host = "";
|
||||
String internalName = username;
|
||||
String domainContext = defaultNamingContext;
|
||||
int index = username.indexOf("\\");
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
String domain = username.substring(0, index);
|
||||
|
||||
username = username.substring(index + 1);
|
||||
internalName = domain.toLowerCase().concat("/").concat(username);
|
||||
|
||||
ActiveDirectoryDomain d = domainMap.get(domain.toUpperCase());
|
||||
|
||||
if (d != null)
|
||||
{
|
||||
domainContext = d.getDomainContext();
|
||||
host = Util.nonNull(d.getHost());
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("could not find domain {}", domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("try to autenticate user {} in context {}", username,
|
||||
domainContext);
|
||||
}
|
||||
|
||||
String dn = getDnOfUserOrGroup(domainContext, username);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("found user at {}", dn);
|
||||
}
|
||||
|
||||
// now we got the DN of the user
|
||||
IADsOpenDSObject dso = COM4J.getObject(IADsOpenDSObject.class, "LDAP:",
|
||||
@@ -262,14 +307,21 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
|
||||
try
|
||||
{
|
||||
IADsUser usr = dso.openDSObject("LDAP://" + dn, dn, password,
|
||||
if (Util.isNotEmpty(host))
|
||||
{
|
||||
host = host.concat("/");
|
||||
}
|
||||
|
||||
IADsUser usr = dso.openDSObject("LDAP://".concat(host).concat(dn), dn,
|
||||
password,
|
||||
0).queryInterface(IADsUser.class);
|
||||
|
||||
if (usr != null)
|
||||
{
|
||||
if (!usr.accountDisabled())
|
||||
{
|
||||
User user = new User(username, usr.fullName(), usr.emailAddress());
|
||||
User user = new User(internalName, usr.fullName(),
|
||||
usr.emailAddress());
|
||||
|
||||
user.setType(TYPE);
|
||||
result = new AuthenticationResult(user, getGroups(usr));
|
||||
@@ -292,10 +344,90 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param rootDSE
|
||||
*/
|
||||
private void readDomains(IADs rootDSE)
|
||||
{
|
||||
try
|
||||
{
|
||||
String configNC = (String) rootDSE.get("configurationNamingContext");
|
||||
|
||||
if (Util.isNotEmpty(configNC))
|
||||
{
|
||||
_Command cmd = ClassFactory.createCommand();
|
||||
|
||||
cmd.activeConnection(con);
|
||||
cmd.commandText("<LDAP://" + configNC
|
||||
+ ">;(NETBIOSName=*);cn,dnsRoot,ncname;subTree");
|
||||
|
||||
_Recordset rs = cmd.execute(null, Variant.MISSING, -1 /* default */);
|
||||
|
||||
while (!rs.eof())
|
||||
{
|
||||
String cn = getString(rs, "cn");
|
||||
String dn = getString(rs, "ncname");
|
||||
String host = getFirstString(rs, "dnsRoot");
|
||||
|
||||
if (Util.isNotEmpty(cn) && Util.isNotEmpty(dn))
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("found domain: {}, {}, {}", new Object[] { cn, dn,
|
||||
host });
|
||||
}
|
||||
|
||||
domainMap.put(cn, new ActiveDirectoryDomain(cn, dn, host));
|
||||
}
|
||||
|
||||
rs.moveNext();
|
||||
}
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("could not find a valid configurationNamingContext");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("could not read domains", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Get the first String of a multivalue recordset item
|
||||
*
|
||||
*
|
||||
* @param rs
|
||||
* @param item
|
||||
*
|
||||
* @return the first item of a recordset
|
||||
*/
|
||||
private String getFirstString(_Recordset rs, String item)
|
||||
{
|
||||
String result = null;
|
||||
Object[] values = (Object[]) getObject(rs, item);
|
||||
|
||||
if (Util.isNotEmpty(values))
|
||||
{
|
||||
Object value = values[0];
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
result = value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all groupnames of a user
|
||||
*
|
||||
*
|
||||
* @param usr
|
||||
@@ -319,6 +451,55 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param rs
|
||||
* @param item
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Object getObject(_Recordset rs, String item)
|
||||
{
|
||||
Object value = null;
|
||||
Fields fields = rs.fields();
|
||||
|
||||
if (fields != null)
|
||||
{
|
||||
Field field = fields.item(item);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
value = field.value();
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param rs
|
||||
* @param item
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getString(_Recordset rs, String item)
|
||||
{
|
||||
String result = null;
|
||||
Object value = getObject(rs, item);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
result = value.toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@@ -326,4 +507,8 @@ public class ActiveDirectoryAuthenticationHandler
|
||||
|
||||
/** Field description */
|
||||
private String defaultNamingContext;
|
||||
|
||||
/** Field description */
|
||||
private Map<String, ActiveDirectoryDomain> domainMap =
|
||||
new HashMap<String, ActiveDirectoryDomain>();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.activedirectory.auth;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sdorra
|
||||
*/
|
||||
public class ActiveDirectoryDomain
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
public ActiveDirectoryDomain() {}
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param domain
|
||||
* @param dn
|
||||
* @param host
|
||||
*/
|
||||
public ActiveDirectoryDomain(String domain, String dn, String host)
|
||||
{
|
||||
this.host = host;
|
||||
this.domain = domain;
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDn()
|
||||
{
|
||||
return dn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDomain()
|
||||
{
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDomainContext()
|
||||
{
|
||||
String dc = dn;
|
||||
|
||||
if (Util.isNotEmpty(host))
|
||||
{
|
||||
dc = host.concat("/").concat(dn);
|
||||
}
|
||||
|
||||
return dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getHost()
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param dn
|
||||
*/
|
||||
public void setDn(String dn)
|
||||
{
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param domain
|
||||
*/
|
||||
public void setDomain(String domain)
|
||||
{
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param host
|
||||
*/
|
||||
public void setHost(String host)
|
||||
{
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String dn;
|
||||
|
||||
/** Field description */
|
||||
private String domain;
|
||||
|
||||
/** Field description */
|
||||
private String host;
|
||||
}
|
||||
@@ -434,6 +434,21 @@
|
||||
</dependencies>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>active-directory</id>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>sonia.scm.plugins</groupId>
|
||||
<artifactId>scm-activedirectory-auth-plugin</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>it</id>
|
||||
|
||||
Reference in New Issue
Block a user