mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 21:45:50 +01:00
(refs #245)Add full name attribute for LDAP authentication.
This commit is contained in:
@@ -33,6 +33,7 @@ trait SystemSettingsControllerBase extends ControllerBase with FlashMapSupport {
|
|||||||
"bindPassword" -> trim(label("Bind Password", optional(text()))),
|
"bindPassword" -> trim(label("Bind Password", optional(text()))),
|
||||||
"baseDN" -> trim(label("Base DN", text(required))),
|
"baseDN" -> trim(label("Base DN", text(required))),
|
||||||
"userNameAttribute" -> trim(label("User name attribute", text(required))),
|
"userNameAttribute" -> trim(label("User name attribute", text(required))),
|
||||||
|
"fullNameAttribute" -> trim(label("Full name attribute", optional(text()))),
|
||||||
"mailAttribute" -> trim(label("Mail address attribute", text(required))),
|
"mailAttribute" -> trim(label("Mail address attribute", text(required))),
|
||||||
"tls" -> trim(label("Enable TLS", optional(boolean()))),
|
"tls" -> trim(label("Enable TLS", optional(boolean()))),
|
||||||
"keystore" -> trim(label("Keystore", optional(text())))
|
"keystore" -> trim(label("Keystore", optional(text())))
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ trait AccountService {
|
|||||||
*/
|
*/
|
||||||
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String) = {
|
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String) = {
|
||||||
LDAPUtil.authenticate(settings.ldap.get, userName, password) match {
|
LDAPUtil.authenticate(settings.ldap.get, userName, password) match {
|
||||||
case Right(mailAddress) => {
|
case Right(ldapUserInfo) => {
|
||||||
// Create or update account by LDAP information
|
// Create or update account by LDAP information
|
||||||
getAccountByUserName(userName) match {
|
getAccountByUserName(userName) match {
|
||||||
case Some(x) => updateAccount(x.copy(mailAddress = mailAddress))
|
case Some(x) => updateAccount(x.copy(mailAddress = ldapUserInfo.mailAddress, fullName = ldapUserInfo.fullName))
|
||||||
case None => createAccount(userName, "", userName, mailAddress, false, None)
|
case None => createAccount(userName, "", ldapUserInfo.fullName, ldapUserInfo.mailAddress, false, None)
|
||||||
}
|
}
|
||||||
getAccountByUserName(userName)
|
getAccountByUserName(userName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ trait SystemSettingsService {
|
|||||||
ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x))
|
ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x))
|
||||||
props.setProperty(LdapBaseDN, ldap.baseDN)
|
props.setProperty(LdapBaseDN, ldap.baseDN)
|
||||||
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
|
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
|
||||||
|
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
|
||||||
props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute)
|
props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute)
|
||||||
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
|
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
|
||||||
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
|
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
|
||||||
@@ -71,6 +72,7 @@ trait SystemSettingsService {
|
|||||||
getOptionValue(props, LdapBindPassword, None),
|
getOptionValue(props, LdapBindPassword, None),
|
||||||
getValue(props, LdapBaseDN, ""),
|
getValue(props, LdapBaseDN, ""),
|
||||||
getValue(props, LdapUserNameAttribute, ""),
|
getValue(props, LdapUserNameAttribute, ""),
|
||||||
|
getOptionValue(props, LdapFullNameAttribute, None),
|
||||||
getValue(props, LdapMailAddressAttribute, ""),
|
getValue(props, LdapMailAddressAttribute, ""),
|
||||||
getOptionValue[Boolean](props, LdapTls, None),
|
getOptionValue[Boolean](props, LdapTls, None),
|
||||||
getOptionValue(props, LdapKeystore, None)))
|
getOptionValue(props, LdapKeystore, None)))
|
||||||
@@ -101,6 +103,7 @@ object SystemSettingsService {
|
|||||||
bindPassword: Option[String],
|
bindPassword: Option[String],
|
||||||
baseDN: String,
|
baseDN: String,
|
||||||
userNameAttribute: String,
|
userNameAttribute: String,
|
||||||
|
fullNameAttribute: Option[String],
|
||||||
mailAttribute: String,
|
mailAttribute: String,
|
||||||
tls: Option[Boolean],
|
tls: Option[Boolean],
|
||||||
keystore: Option[String])
|
keystore: Option[String])
|
||||||
@@ -134,6 +137,7 @@ object SystemSettingsService {
|
|||||||
private val LdapBindPassword = "ldap.bind_password"
|
private val LdapBindPassword = "ldap.bind_password"
|
||||||
private val LdapBaseDN = "ldap.baseDN"
|
private val LdapBaseDN = "ldap.baseDN"
|
||||||
private val LdapUserNameAttribute = "ldap.username_attribute"
|
private val LdapUserNameAttribute = "ldap.username_attribute"
|
||||||
|
private val LdapFullNameAttribute = "ldap.fullname_attribute"
|
||||||
private val LdapMailAddressAttribute = "ldap.mail_attribute"
|
private val LdapMailAddressAttribute = "ldap.mail_attribute"
|
||||||
private val LdapTls = "ldap.tls"
|
private val LdapTls = "ldap.tls"
|
||||||
private val LdapKeystore = "ldap.keystore"
|
private val LdapKeystore = "ldap.keystore"
|
||||||
|
|||||||
@@ -18,51 +18,49 @@ object LDAPUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Try authentication by LDAP using given configuration.
|
* Try authentication by LDAP using given configuration.
|
||||||
* Returns Right(mailAddress) if authentication is successful, otherwise Left(errorMessage).
|
* Returns Right(LDAPUserInfo) if authentication is successful, otherwise Left(errorMessage).
|
||||||
*/
|
*/
|
||||||
def authenticate(ldapSettings: Ldap, userName: String, password: String): Either[String, String] = {
|
def authenticate(ldapSettings: Ldap, userName: String, password: String): Either[String, LDAPUserInfo] = {
|
||||||
bind(
|
bind(
|
||||||
ldapSettings.host,
|
host = ldapSettings.host,
|
||||||
ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
|
port = ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
|
||||||
ldapSettings.bindDN.getOrElse(""),
|
dn = ldapSettings.bindDN.getOrElse(""),
|
||||||
ldapSettings.bindPassword.getOrElse(""),
|
password = ldapSettings.bindPassword.getOrElse(""),
|
||||||
ldapSettings.tls.getOrElse(false),
|
tls = ldapSettings.tls.getOrElse(false),
|
||||||
ldapSettings.keystore.getOrElse("")
|
keystore = ldapSettings.keystore.getOrElse(""),
|
||||||
) match {
|
error = "System LDAP authentication failed."
|
||||||
case Some(conn) => {
|
){ conn =>
|
||||||
withConnection(conn) { conn =>
|
|
||||||
findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match {
|
findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match {
|
||||||
case Some(userDN) => userAuthentication(ldapSettings, userDN, password)
|
case Some(userDN) => userAuthentication(ldapSettings, userDN, userName, password)
|
||||||
case None => Left("User does not exist.")
|
case None => Left("User does not exist.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case None => Left("System LDAP authentication failed.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def userAuthentication(ldapSettings: Ldap, userDN: String, password: String): Either[String, String] = {
|
private def userAuthentication(ldapSettings: Ldap, userDN: String, userName: String, password: String): Either[String, LDAPUserInfo] = {
|
||||||
bind(
|
bind(
|
||||||
ldapSettings.host,
|
host = ldapSettings.host,
|
||||||
ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
|
port = ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
|
||||||
userDN,
|
dn = userDN,
|
||||||
password,
|
password = password,
|
||||||
ldapSettings.tls.getOrElse(false),
|
tls = ldapSettings.tls.getOrElse(false),
|
||||||
ldapSettings.keystore.getOrElse("")
|
keystore = ldapSettings.keystore.getOrElse(""),
|
||||||
) match {
|
error = "User LDAP Authentication Failed."
|
||||||
case Some(conn) => {
|
){ conn =>
|
||||||
withConnection(conn) { conn =>
|
|
||||||
findMailAddress(conn, userDN, ldapSettings.mailAttribute) match {
|
findMailAddress(conn, userDN, ldapSettings.mailAttribute) match {
|
||||||
case Some(mailAddress) => Right(mailAddress)
|
case Some(mailAddress) => Right(LDAPUserInfo(
|
||||||
|
userName = userName,
|
||||||
|
fullName = ldapSettings.fullNameAttribute.flatMap { fullNameAttribute =>
|
||||||
|
findFullName(conn, userDN, fullNameAttribute)
|
||||||
|
}.getOrElse(userName),
|
||||||
|
mailAddress = mailAddress))
|
||||||
case None => Left("Can't find mail address.")
|
case None => Left("Can't find mail address.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case None => Left("User LDAP Authentication Failed.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def bind(host: String, port: Int, dn: String, password: String, tls: Boolean, keystore: String): Option[LDAPConnection] = {
|
private def bind[A](host: String, port: Int, dn: String, password: String, tls: Boolean, keystore: String, error: String)
|
||||||
|
(f: LDAPConnection => Either[String, A]): Either[String, A] = {
|
||||||
if (tls) {
|
if (tls) {
|
||||||
// Dynamically set Sun as the security provider
|
// Dynamically set Sun as the security provider
|
||||||
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider())
|
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider())
|
||||||
@@ -87,7 +85,9 @@ object LDAPUtil {
|
|||||||
// Bind to the server
|
// Bind to the server
|
||||||
conn.bind(LDAP_VERSION, dn, password.getBytes)
|
conn.bind(LDAP_VERSION, dn, password.getBytes)
|
||||||
|
|
||||||
Some(conn)
|
// Execute a given function and returns a its result
|
||||||
|
f(conn)
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
case e: Exception => {
|
case e: Exception => {
|
||||||
// Provide more information if something goes wrong
|
// Provide more information if something goes wrong
|
||||||
@@ -96,20 +96,15 @@ object LDAPUtil {
|
|||||||
if (conn.isConnected) {
|
if (conn.isConnected) {
|
||||||
conn.disconnect()
|
conn.disconnect()
|
||||||
}
|
}
|
||||||
|
// Returns an error message
|
||||||
None
|
Left(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def withConnection[T](conn: LDAPConnection)(f: LDAPConnection => T): T = {
|
/**
|
||||||
try {
|
* Search a specified user and returns userDN if exists.
|
||||||
f(conn)
|
*/
|
||||||
} finally {
|
|
||||||
conn.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = {
|
private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = {
|
||||||
@tailrec
|
@tailrec
|
||||||
def getEntries(results: LDAPSearchResults, entries: List[Option[LDAPEntry]] = Nil): List[LDAPEntry] = {
|
def getEntries(results: LDAPSearchResults, entries: List[Option[LDAPEntry]] = Nil): List[LDAPEntry] = {
|
||||||
@@ -134,4 +129,14 @@ object LDAPUtil {
|
|||||||
Option(results.next.getAttribute(mailAttribute)).map(_.getStringValue)
|
Option(results.next.getAttribute(mailAttribute)).map(_.getStringValue)
|
||||||
} else None
|
} else None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def findFullName(conn: LDAPConnection, userDN: String, nameAttribute: String): Option[String] =
|
||||||
|
defining(conn.search(userDN, LDAPConnection.SCOPE_BASE, null, Array[String](nameAttribute), false)){ results =>
|
||||||
|
if(results.hasMore) {
|
||||||
|
Option(results.next.getAttribute(nameAttribute)).map(_.getStringValue)
|
||||||
|
} else None
|
||||||
|
}
|
||||||
|
|
||||||
|
case class LDAPUserInfo(userName: String, fullName: String, mailAddress: String)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,13 @@
|
|||||||
<span id="error-ldap_userNameAttribute" class="error"></span>
|
<span id="error-ldap_userNameAttribute" class="error"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label" for="ldapFullNameAttribute">Full name attribute</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" id="ldapFullNameAttribute" name="ldap.fullNameAttribute" value="@settings.ldap.map(_.fullNameAttribute)"/>
|
||||||
|
<span id="error-ldap_fullNameAttribute" class="error"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label" for="ldapMailAttribute">Mail address attribute</label>
|
<label class="control-label" for="ldapMailAttribute">Mail address attribute</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
|||||||
Reference in New Issue
Block a user