Как запросить LDAP из C# для разрешения имени хоста Oracle TNS при использовании управляемого ODP.NET?
В дополнение к моему предыдущему Вопросу, на который мне удалось ответить самому с помощью форумов Oracle, у меня теперь есть еще одна проблема, которая вытекает из предыдущего (предоставлена для справки).
Я хочу запросить LDAP непосредственно из моего кода C#, чтобы выполнить поиск LDAP имени хоста Oracle TNS, чтобы получить строку подключения. Обычно это хранится в tnsnames.ora, и моя организация использует LDAP (через ldap.ora) для разрешения имен хостов с сервера LDAP с использованием Active Directory.
Тем не менее, я использую ODP.NET, бета-версию управляемого драйвера (Oracle.ManagedDataAccess.dll) в своем приложении C#, которое не поддерживает LDAP, как упомянуто в заметках о выпуске, указанных в ответе форума Oracle, о котором я упоминал ранее. Вот почему я хочу запросить LDAP напрямую из C#.
Я нашел способ сделать это здесь, используя DirectoryEntry
а также DirectorySearcher
, но я понятия не имею, что поставить в качестве параметров для DirectorySearcher
, У меня есть доступ к ldap.ora в следующем формате:
# LDAP.ORA Configuration
# Сгенерировано средствами конфигурации Oracle.
DEFAULT_ADMIN_CONTEXT = "dc = xx, dc = mycompany, dc = com"
DIRECTORY_SERVERS = (ldap_server1.mycompany.com:389:636,ldap_server2.mycompany.com:389:636,...) DIRECTORY_SERVER_TYPE = OID
Но как мне сопоставить это с настройкой запроса LDAP в моем коде C#?
4 ответа
Судя по тому, что я нашел в Oracle Database Name Resolution с OpenLDAP, код должен выглядеть примерно так:
string directoryServer = "ldap_server1.mycompany.com:389";
string defaultAdminContext = "dc=xx,dc=mycompany,dc=com";
string oracleHostEntryPath = string.Format("LDAP://{0}/cn=OracleContext,{1}", directoryServer, defaultAdminContext);
var directoryEntry = new DirectoryEntry(oracleHostEntryPath) {AuthenticationType = AuthenticationTypes.None};
var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=ABCDEFG1))", new[] { "orclnetdescstring" }, SearchScope.Subtree);
string oracleNetDescription = Encoding.Default.GetString(des.FindOne().Properties["orclnetdescstring"][0] as byte[]);
В дополнение к моему второму комментарию в принятом Ответе, это код для выполнения поиска LDAP, который улучшает оригинальную версию, которую я нашел здесь. Он также обрабатывает списки серверов в файле ldap.ora, который содержит несколько номеров портов с разделителями.
private static string ResolveServiceNameLdap(string serviceName)
{
string tnsAdminPath = Path.Combine(@"C:\Apps\oracle\network\admin", "ldap.ora");
string connectionString = string.Empty;
// ldap.ora can contain many LDAP servers
IEnumerable<string> directoryServers = null;
if (File.Exists(tnsAdminPath))
{
string defaultAdminContext = string.Empty;
using (var sr = File.OpenText(tnsAdminPath))
{
string line;
while ((line = sr.ReadLine()) != null)
{
// Ignore commetns
if (line.StartsWith("#"))
{
continue;
}
// Ignore empty lines
if (line == string.Empty)
{
continue;
}
// If line starts with DEFAULT_ADMIN_CONTEXT then get its value
if (line.StartsWith("DEFAULT_ADMIN_CONTEXT"))
{
defaultAdminContext = line.Substring(line.IndexOf('=') + 1).Trim(new[] {'\"', ' '});
}
// If line starts with DIRECTORY_SERVERS then get its value
if (line.StartsWith("DIRECTORY_SERVERS"))
{
string[] serversPorts = line.Substring(line.IndexOf('=') + 1).Trim(new[] {'(', ')', ' '}).Split(',');
directoryServers = serversPorts.SelectMany(x =>
{
// If the server includes multiple port numbers, this needs to be handled
string[] serverPorts = x.Split(':');
if (serverPorts.Count() > 1)
{
return serverPorts.Skip(1).Select(y => string.Format("{0}:{1}", serverPorts.First(), y));
}
return new[] {x};
});
}
}
}
// Iterate through each LDAP server, and try to connect
foreach (string directoryServer in directoryServers)
{
// Try to connect to LDAP server with using default admin contact
try
{
var directoryEntry = new DirectoryEntry("LDAP://" + directoryServer + "/" + defaultAdminContext, null, null, AuthenticationTypes.Anonymous);
var directorySearcher = new DirectorySearcher(directoryEntry, "(&(objectclass=orclNetService)(cn=" + serviceName + "))", new[] { "orclnetdescstring" }, SearchScope.Subtree);
SearchResult searchResult = directorySearcher.FindOne();
var value = searchResult.Properties["orclnetdescstring"][0] as byte[];
if (value != null)
{
connectionString = Encoding.Default.GetString(value);
}
// If the connection was successful, then not necessary to try other LDAP servers
break;
}
catch
{
// If the connection to LDAP server not successful, try to connect to the next LDAP server
continue;
}
}
// If casting was not successful, or not found any TNS value, then result is an error message
if (string.IsNullOrEmpty(connectionString))
{
connectionString = "TNS value not found in LDAP";
}
}
else
{
// If ldap.ora doesn't exist, then return error message
connectionString = "ldap.ora not found";
}
return connectionString;
}
Вот более полный пример, основанный на других ответах:
using System;
using System.Data;
using System.DirectoryServices;
using System.Text;
using Oracle.ManagedDataAccess.Client;
namespace BAT
{
internal class Program
{
private static void Main(string[] args)
{
string directoryServer = "myServer:389";
string defaultAdminContext = "cn=OracleContext,dc=world";
string serviceName = "MYDB";
string userId = "MYUSER";
string password = "MYPW";
using (IDbConnection connection = GetConnection(directoryServer, defaultAdminContext, serviceName, userId,
password))
{
connection.Open();
connection.Close();
}
}
private static IDbConnection GetConnection(string directoryServer, string defaultAdminContext,
string serviceName, string userId, string password)
{
string descriptor = ConnectionDescriptor(directoryServer, defaultAdminContext, serviceName);
// Connect to Oracle
string connectionString = $"user id={userId};password={password};data source={descriptor}";
OracleConnection con = new OracleConnection(connectionString);
return con;
}
private static string ConnectionDescriptor(string directoryServer, string defaultAdminContext,
string serviceName)
{
string ldapAdress = $"LDAP://{directoryServer}/{defaultAdminContext}";
string query = $"(&(objectclass=orclNetService)(cn={serviceName}))";
string orclnetdescstring = "orclnetdescstring";
DirectoryEntry directoryEntry = new DirectoryEntry(ldapAdress, null, null, AuthenticationTypes.Anonymous);
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry, query, new[] { orclnetdescstring },
SearchScope.Subtree);
SearchResult searchResult = directorySearcher.FindOne();
byte[] value = searchResult.Properties[orclnetdescstring][0] as byte[];
if (value != null)
{
string descriptor = Encoding.Default.GetString(value);
return descriptor;
}
throw new Exception("Error querying LDAP");
}
}
}
Чтобы начать, попробуйте:
using System.DirectoryServices;
...
DirectoryEntry de = new DirectoryEntry();
de.Username = "username@mysite.com";
de.Password = "password";
de.Path = "LDAP://DC=mysite,DC=com";
de.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher des = new DirectorySearcher(de);
Теперь вы сможете использовать ldap, используя DirectorySearcher (фильтр, findone и т. Д.), Описанный здесь.