LDAP İle KİMLİK DOĞRULAMA ve YETKİLENDİRME (ORACLE & MICROSOFT)
Bu ne işe yarar diye bir okurum haklı olarak sormuş. Ben de ona yazdığım cevabı buraya yazayım ki herkes yararlansın..
Örneğin Bir şirketin içindesiniz başka ülkelerde de ofisleriniz var ve hepsi aynı şirket içinde aynı network de çalışıyorsunuz ki çoğu şirket böyledir..Bir çok yazılımınız var .. Bu yazılımların içinde şirkete giren veya çıkan yani şirketten ayrılan kişiler olabilir veya kullandıkları ortak yazılımlar olabilir .. Windows Active Directory niz var sa LDAP olarak çalışır zaten.. Veya Oracle olabilir.. Giren tüm kişileri bu LDAP a kaydedip bağlanacakları yazıcılar vs ile haklari dahi ne konumda olduklari vs vs buraya özel şekilde işte bu yazılımlarla bu dokumanı okursan yaparsin.. Burada kayıt yapmak demek örneğin kişinin muhasebe de veya insan kaynaklarinda kullanacağı yazılımlara da giriş hakkıı verilmesi veya verilmemesi demektir.. Fabrika nız var ayrıca ve fabrikadan diyelim bir işçi atıldı onu LDAP ile sistemden disable yaparsaniz artik IK yazilimina bagli PDKS yani personel devamlilik kontrol sisteminde kart basarsa dahi içeri giremez.. Muhasebe den ayrildiysa artik muhasebe programina giremeyeceği gibi kapidan da giremez.. Bunu çoğaltabilirsin.. Ayrıca network de ki haklari vs vs ..Tüm yazılımları LDAP üzerinden Active Directory ye entegre edersen işte tüm bunlari başın ağrımadan yaparsın..
Yine kurumsal şirket olarak Email çözümünde Microsoft Exchange Server kullanılıyorsa o zaman zaten Active Directory kurulu olması gerekir.. Exchange Server Active Directory yi yoğun olarak kullanmaktadır.. Çalışanların emailleri de işte bu LDAP bağlantısı ile Active Directory üzerinden yapılarak Exchange Server da açılabilir veya tam tersi disable edilebilir veya silinebilir..
Kimlik doğrulama(authentication) ve yetkilendirme(authorization) için LDAP protokolünü nasıl kullanırız.
LDAP, OpenLDAP, Microsoft Active Directory gibi dizin sunucuları tarafından kullanılmaktadır.
LDAP (Lightweight Directory Access Protocol)
LDAP, TCP/IP üzerinde çalışan dizin servislerini sorgulama ve değiştirme amacıyla kullanılan bir uygulama katmanı protokolüdür. LDAP tüm kullanıcılarının en hızlı şekilde ve sürekli olarak bilgiye ulaşmalarını sağlayabilmek için, hızlı, ölçeklenebilir ve yönetilebilir bir ağ altyapısı kurmak için oluşturulmuştur. Bilgisayarların yoğun olarak kullanıldığı ağ üzerindeki IP (Internet Protocol) adres ayarlarının otomatik olarak gerçekleştirilebilmesi amacıyla LDAP kayıtlarını kullanan bir otomatik IP atama sistemi kullanılır.
LDAP üç kategori içinde tanımlanabilecek 9 temel protokol operasyonuna sahiptir:
- Sorgulama Operasyonları: arama ve karşılaştırma (search, compare)
- Güncelleme Operasyonları: ekleme, silme, güncelleme, yeniden isimlendirme (add,delete, modify, modify DN (rename))
- Kimlik doğrulama (authentication) ve kontrol operasyonları: bağlanma, bağlantıyı kesme ve bağlantı iptali (bind, unbind, abandon.)
Yazılımınızda LDAP operasyonlarından güncelleme operasyonları dışında, sorgulama, kimlik doğrulama ve kontrol operasyonlarında da yararlanabilirsiniz.
Buna göre de web.config appSettings te LDAP yapılandırma ayarları aşağıdaki gibi tutulur.
<appSettings>
<add key=”LDAPHost” value=”192.168.1.43″ />
<add key=”LDAPPort” value=”3060″ />
<add key=”LDAPUsersDN” value=”cn=Users,dc=sirketadi,dc=com,dc=tr” />
<add key=”LDAPRolesDN” value=”cn=Groups,dc= sirketadi,dc=com,dc=tr” />
<add key=”LDAPUser” value=”oadmin” />
<add key=”LDAPPass” value=”” />
<add key=”LDAPConnType” value=”Basic” />
<add key=”LDAPType” value=”Oracle” />
</appSettings>
LDAP ta bilgiler DIT (Directory Information Tree) denen bir ağaç şeklindeki yapıda tutulmaktadır.
DIT deki herbir entry, entry’i açıklayan bir ve/veya daha fazla “attribute” içerir ve her bir attribute bir “type”ve “value” içermektedir. Directory entry’si bazı object’leri tanımlar ve entry de kullanılabilecek attribute’ları belirler.
LDAP Attribute | Example |
CN – Common Name | CN=Guy Thomas. Actually, this LDAP attribute is made up from givenName joined to SN. Kişinin tam adını içerir. |
description | What you see in Active Directory Users and Computers. Not to be confused with displayName on the Users property sheet. Kişi ile ilgili olarak yazılmış olan açıklamayı içerir. |
displayName | displayName = Guy Thomas. If you script this property, be sure you understand which field you are configuring. DisplayName can be confused with CN or description. Kişinin görüntülenecek ismini içerir. |
DN – also distinguishedName | DN is simply the most important LDAP attribute. CN=Jay Jamieson, OU= Newport,DC=cp,DC=com Kişinin LDAP tanımlamasını içerir. |
givenName | Firstname also called Christian name Kişinin adını içerir. |
homeDrive | Home Folder : connect. Tricky to configure |
name | name = Guy Thomas. Exactly the same as CN. Kişinin tam adını içerir. CN ile aynıdır. |
objectCategory | Defines the Active Directory Schema category. For example, objectCategory = Person Active Directory nesnesinin kategorisini içerir. |
objectClass | objectClass = User. Also used for Computer, organizationalUnit, even container. Important top level container. |
physicalDeliveryOfficeName | Office! on the user’s General property sheet |
profilePath | Roaming profile path: connect. Trick to set up |
sAMAccountName | sAMAccountName = guyt. Old NT 4.0 logon name, must be unique in the domain. Can be confused with CN. |
SN | SN = Thomas. This would be referred to as last name or surname. Kişinin soyadını içerir. |
userAccountControl | Used to disable an account. A value of 514 disables the account, while 512 makes the account ready for logon. |
userPrincipalName | userPrincipalName = guyt@CP.com Often abbreviated to UPN, and looks like an email address. Very useful for logging on especially in a large Forest. Note UPN must be unique in the forest. |
Kimlik Doğrulama
protected void Authenticate(string user, string pass, bool remember)
{
var secLib = new SecurityLib(Session, Page);
var res = new AuthResult();
var auth = secLib.AuthResult(user, pass, ref res);
if (!auth.Result)
{
secLib.CreateLoginLog(user, false);
lgn_Login.FailureText = auth.ResultMessage;
return;
}
secLib.CreateLoginLog(user, true);
ProceedLogin(res, remember);
FormsAuthentication.RedirectFromLoginPage(res.UserName, remember);
}
Class yapılarını aşağıdaki resimde bulabilirsiniz
SecurityLib class ı altında bulunan AuthResult methodu ile kullanıcının girişinin başarılı olup olmadığı bilgisi alınır. Eğer kullanıcı girişi başarılı ise anasayfaya yönlendirme yapılır. Kullanıcı girişi başarılı değilse de kullanıcıya gerekli mesaj gösterilir. Örneğin Your account has been locked.
Kimlik doğrulamaları daha önce bahsi geçen attribute değerleriyle yapılmaktadır.
Size 2 class ın iç yapısını vereyim: AuthResult ve Auth illa olması gereken class lar değil. Eğer büyük bir ERP vb yazılımı yazıyorsanız kendi veritabanınızda kullanıcılar ve rolleri vb olmalı. LDAP dan gelen veriyi veritabanınızdaki aynı bilgiye sahip kullanıcıyı ve rollerini bulmak için kullanabilirsiniz. Bu yapı örneğin menü veya web sayfalarınızın hangi kişi veya rollere açık kapalı olup olmadığını sorgulamanız için yapılır. O nedenle bu 2 class ı verdim. İsterseniz hiç kullanmayabilir ve aşağıdaki kodlarda LDAP Oracle ve LDAP Microsoft kodlarındaki bunlarla ilgili o satırları silebilirsiniz.. Örneğin FindUserRoles bunlardan biri.
public class AuthResult
{
public string UserName;
public string Domain;
public string SID;
public List<int> UserFunctions = new List<int>();
public List<int> DeniedUserFunctions = new List<int>();
public List<string> Roles = new List<string>();
public string Result;
public long ActorID;
}
public class Auth
{
public bool Result { get; set; }
public string ResultMessage { get; set; }
}
Ayrıca seclib isimli class ınızdaki burada size lazım olan 2 metod olan AuthResult ( yukarıdaki class ismi ile aynı ama o bir class burada bahsettiğim sizin oluşturacağınız başka bir class da ki örneğin seclib class ında oluşturacağınız aynı isimdeki bir metod. Karıştırmayın ikisini) ve Authentication ı da burada kodlarıyla vereyim..
public Auth AuthResult(string user, string pass, ref AuthResult result)
{
var auth = new Auth();
switch (GetLDAPType())
{
case LDAPType.Microsoft:
auth.Result = Authenticate(user, pass, ref result);
auth.ResultMessage = GetAuthResultMessages(account);
break;
case LDAPType.Oracle:
auth.Result = Authenticate(user, pass, ref result);
if (!auth.Result)
auth.ResultMessage = “Username or password incorrect.”;
break;
}
return auth;
}
public bool Authenticate(string user, string pass, ref AuthResult Result)
{
if (Result == null) throw new ArgumentNullException(“Result”);
if (CheckIsAdmin(user, pass))
{
_session[“ISADMIN”] = 1;
Result.SID = user;
Result.Result = “”;
Result.UserName = user;
account = AccountControl.Success;
return true;
}
if (ConfigurationManager.AppSettings[“CheckAuths”] != null)
{
if (ConfigurationManager.AppSettings[“CheckAuths”] == “False”)
{
_session[“ISADMIN”] = 1;
}
}
Result.Roles = new System.Collections.Generic.List<string>();
bool result;
try
{
switch (this.GetLDAPType())
{
case LDAPType.None:
break;
case LDAPType.Microsoft:
{
string path = string.Concat(new string[]
{
“LDAP://”,
GetAppConfigItem(“LDAPHost”), “:”, GetAppConfigItem(“LDAPPort”), GetAppConfigItem(“LDAPUsersDN”)
});
try
{
var searchRoot2 = new DirectoryEntry(AdPath, ((this.GetAppConfigItem(“LDAPDomain”).Trim() == “”) ? “” : (this.GetAppConfigItem(“LDAPDomain”).Trim() + “\\”)) + user, pass, this.GetUserAuthType());
var directorySearcher2 = new DirectorySearcher(searchRoot2, string.Concat(new string[] { “(&(objectClass=user)(|(userPrincipalName=”, user, “)(userPrincipalName=”, user, “@*)))” }));
using (directorySearcher2)
{
DirectoryEntry directoryEntry2 =
directorySearcher2.FindOne().GetDirectoryEntry();
using (directoryEntry2)
{
Result.UserName =
directoryEntry2.Properties[“userPrincipalName”].Value.ToString();
if (Result.UserName.Contains(‘@’))
{
Result.UserName = Result.UserName.Substring(0, Result.UserName.IndexOf(‘@’));
}
var securityIdentifier =
new System.Security.Principal.SecurityIdentifier(
(byte[])directoryEntry2.Properties[“objectSid”].Value, 0);
Result.SID = securityIdentifier.Value;
account = AccountControl.Success;
}
}
Result.Result = “”;
FindUserRoles(Result.SID, ref Result);
}
catch
{
account = AccountControl.UnKnown;
result = false;
return result;
}
break;
}
case LDAPType.Oracle:
try
{
var identifier = new LdapDirectoryIdentifier(GetAppConfigItem(“LDAPHost”), Convert.ToInt32(this.GetAppConfigItem(“LDAPPort”)));
var ldapConnection = new LdapConnection(identifier);
using (ldapConnection)
{
ldapConnection.AuthType = AuthType.Basic;
var newCredential = new System.Net.NetworkCredential(“cn=” + user+ “,” + GetAppConfigItem(“LDAPUsersDN”), pass);
ldapConnection.Bind(newCredential);
var searchResponse = (SearchResponse)ldapConnection.SendRequest(new SearchRequest
{
DistinguishedName = “cn=” + user + “,” + this.GetAppConfigItem(“LDAPUsersDN”),
Filter = “(|(objectClass=orclUser)(objectClass=person))”,
Scope = System.DirectoryServices.Protocols.SearchScope.Subtree
});
if (searchResponse != null)
{
var entries = searchResponse.Entries;
const int num = 0;
if (num < entries.Count)
{
var searchResultEntry = entries[num];
string userName = “”;
try
{
object[] values = searchResultEntry.Attributes[“cn”].GetValues(typeof(string));
if (values.Length > 0)
{
userName = values[0].ToString();
}
}
catch (Exception ex)
{
}
Result.Result = “”;
Result.UserName = userName;
Result.SID = searchResultEntry.DistinguishedName.Replace(” “, “”).ToLower(new System.Globalization.CultureInfo(“en-US”));
FindUserRoles(Result.SID, ref Result);
}
}
}
}
catch (LdapException ex)
{
result = false;
}
break;
}
}
catch (LdapException ex)
{
result = false;
}
if (!string.IsNullOrEmpty(Result.SID))
{
account = AccountControl.Success;
result = true;
}
else
result = false;
return result;
}
Oracle LDAP kimlik doğrulama:
public bool Authenticate(string user, string pass, ref AuthResult Result)
{
if (Result == null) throw new ArgumentNullException(“Result”);
switch (this.GetLDAPType())
{
case LDAPType.Oracle:
var identifier = new LdapDirectoryIdentifier(this.GetAppConfigItem(“LDAPHost”), Convert.ToInt32(this.GetAppConfigItem(“LDAPPort”)));
var ldapConnection = new LdapConnection(identifier);
using (ldapConnection)
{
ldapConnection.AuthType = AuthType.Basic;
var newCredential = new System.Net.NetworkCredential(“cn=” + user + “,” + this.GetAppConfigItem(“LDAPUsersDN”), pass);
ldapConnection.Bind(newCredential);
var searchResponse = (SearchResponse)ldapConnection.SendRequest(new SearchRequest
{
DistinguishedName = “cn=” + user + “,” + this.GetAppConfigItem(“LDAPUsersDN”),
Filter = “(|(objectClass=orclUser)(objectClass=person))”,
Scope = System.DirectoryServices.Protocols.SearchScope.Subtree
});
var entries = searchResponse.Entries;
int num = 0;
if (num < entries.Count)
{
var searchResultEntry = entries[num];
string userName = “”;
object[] values = searchResultEntry.Attributes[“cn”].GetValues(typeof(string));
if (values != null && values.Length > 0)
{
userName = values[0].ToString();
}
Result.Result = “”;
Result.UserName = userName;
Result.SID = searchResultEntry.DistinguishedName.Replace(” “, “”).ToLower(new System.Globalization.CultureInfo(“en-US”));
FindUserRoles(Result.SID, ref Result);
}
}
break;
}
}
Microsoft Active Directory kimlik doğrulama:
public bool Authenticate(string user, string pass, ref AuthResult Result)
{
if (Result == null) throw new ArgumentNullException(“Result”);
switch (this.GetLDAPType())
{
case LDAPType.Microsoft:
{
var searchRoot = new DirectoryEntry(ADPath);
var directorySearcher = new DirectorySearcher(searchRoot, “(&(objectClass=user)(|(userPrincipalName=” + user + “)(userPrincipalName=” + user + “@*)))”);
using (directorySearcher)
{
var results = directorySearcher.FindAll();
foreach (SearchResult sResult in results)
{
var directoryEntry = sResult.GetDirectoryEntry();
using (directoryEntry)
{
var userEntry = new DirectoryEntry(ADPath, ((this.GetAppConfigItem(“LDAPDomain”).Trim() == “”) ? “” : (this.GetAppConfigItem(“LDAPDomain”).Trim() + “\\”)) + user, pass, this.GetUserAuthType());
var userSearch = new DirectorySearcher(userEntry, string.Concat(new string[] { “(&(objectClass=user)(|(userPrincipalName=”, user, “)(userPrincipalName=”, user, “@*)))” }));
using (userSearch)
{
userSearch.FindOne();
Result.UserName = userEntry.Username;
if (Result.UserName.Contains(‘@’))
{
Result.UserName = Result.UserName.Substring(0, Result.UserName.IndexOf(‘@’));
}
var securityIdentifier = new System.Security.Principal.SecurityIdentifier((byte[])userEntry.Properties[“objectSid”].Value, 0);
Result.SID = securityIdentifier.Value;
}
}
}
Result.Result = “”;
this.FindUserRoles(Result.SID, ref Result);
break;
}
}
Microsoft Active Directory üzerindeki kullanıcı hesap bilgileri (hesabın kilitli olması, hesabın aktif olmaması, şifre süresinin geçmesi vs.) UserAccountControl özniteliğinin bayrakları kullanılarak alınır. UserAccountControl altındaki bayraklarının tümü değerleriyle birlikte aşağıdaki tablodadır.
Property Flag | HEX | Decimal |
SCRIPT | 0x0001 | 1 |
ACCOUNTDISABLE | 0x0002 | 2 |
HOMEDIR_REQUIRED | 0x0008 | 8 |
LOCKOUT | 0x0010 | 16 |
PASSWD_NOTREQD | 0x0020 | 32 |
PASSWD_CANT_CHANGE P.S. You cannot assign this permission directly by changing the UserAccountControl attribute. For information on setting permission programmatically: https://docs.microsoft.com/en-us/windows/desktop/ADSI/modifying-user-cannot-change-password-ldap-provider | 0x0040 | 64 |
ENCRYPTED_TEXT_PWD_ALLOWED | 0x0080 | 128 |
TEMP_DUPLICATE_ACCOUNT | 0x0100 | 256 |
NORMAL_ACCOUNT | 0x0200 | 512 |
INTERDOMAIN_TRUST_ACCOUNT | 0x0800 | 2048 |
WORKSTATION_TRUST_ACCOUNT | 0x1000 | 4096 |
SERVER_TRUST_ACCOUNT | 0x2000 | 8192 |
DONT_EXPIRE_PASSWORD | 0x10000 | 65536 |
MNS_LOGON_ACCOUNT | 0x20000 | 131072 |
SMARTCARD_REQUIRED | 0x40000 | 262144 |
TRUSTED_FOR_DELEGATION | 0x80000 | 524288 |
NOT_DELEGATED | 0x100000 | 1048576 |
USE_DES_KEY_ONLY | 0x200000 | 2097152 |
DONT_REQ_PREAUTH | 0x400000 | 4194304 |
PASSWORD_EXPIRED | 0x800000 | 8388608 |
TRUSTED_TO_AUTH_FOR_DELEGATION | 0x1000000 | 16777216 |
Örneğin bir yazılımınız var ve kullanıcının hesabının aktif olup olmadığı ACCOUNTDISABLE bayrağının set edilip edilmediği ile kontrol edilir.
private bool IsDisabled(DirectoryEntry de)
{
bool result=false;
const string attrib = “userAccountControl”;
const int ufAccountDisable = 0x0002;
de.RefreshCache(new string[] { attrib });
var flags =(int)de.Properties[attrib].Value;
if (((flags & ufAccountDisable) == ufAccountDisable))
{
result = true;
}
else
{
return false;
}
return result;
}
LOCK_OUT ve PASSWORD_EXPIRED yerine ms-DS-User-Account-Control-Computed adlı özniteliğin UF_LOCKOUT ve UF_PASSWORD_EXPIRED ı kullanılır.
private bool IsLocked(DirectoryEntry de)
{
bool result = false;
const string attrib = “msds-user-account-control-computed”;
const int ufLockout = 0x0010;
de.RefreshCache(new string[] { attrib });
var flags = (int)de.Properties[attrib].Value;
if (((flags & ufLockout) == ufLockout))
{
result = true;
}
else
{
return false;
}
return result;
}
private bool IsPasswordExpired(DirectoryEntry de)
{
bool result = false;
const string attrib = “msds-user-account-control-computed”;
const int UF_PASSWORD_EXPIRED = 0x800000;
de.RefreshCache(new string[] { attrib });
var flags = (int)de.Properties[attrib].Value;
if (((flags & UF_PASSWORD_EXPIRED) == UF_PASSWORD_EXPIRED))
{
result = true;
}
else
{
return false;
}
return result;
}
Hepsi bu kadar. Bu örnek property dediğimiz kısımların kullanımı açısından belki internette hiç bulamayacağınız bir örnektir. Visual Studio üzerinde class ların logical yapılarını resim olarak örnek olması için verdim. Siz kendi yapınıza göre class ları tanımlarsınız. En önemli ve minimum ile çalışmanız için de notlarda lazım olan kodları ve açıklamaları yaptım.
Sağlıcakla kalın.
Selcuk Celik
Meraba bu program ne işe yarıyor yardımcı olurmusunuz
Örneğin Bir şirketin içindesiniz başka ülkelerde de ofisleriniz var ve hepsi aynı şirket içinde aynı network de çalışıyorsunuz ki çoğu şirket böyledir..Bir çok yazılımınız var .. Bu yazılımların içinde şirkete giren veya çıkan yani şirketten ayrılan kişiler olabilir veya kullandıkları ortak yazılımlar olabilir .. Windows Active Directory niz var sa LDAP olarak çalışır zaten.. Veya Oracle olabilir.. Giren tüm kişileri bu LDAP a kaydedip bağlanacakları yazıcılar vs ile haklari dahi ne konumda olduklari vs vs buraya özel şekilde işte bu yazılımlarla bu dokumanı okursan yaparsin.. Burada kayıt yapmak demek örneğin kişinin muhasebe de veya insan kaynaklarinda kullanacağı yazılımlara da giriş hakkıı verilmesi veya verilmemesi demektir.. Fabrika nız var ayrıca ve fabrikadan diyelim bir işçi atıldı onu LDAP ile sistemden disable yaparsaniz artik IK yazilimina bagli PDKS yani personel devamlilik kontrol sisteminde kart basarsa dahi içeri giremez.. Muhasebe den ayrildiysa artik muhasebe programina giremeyeceği gibi kapidan da giremez.. Bunu çoğaltabilirsin..
Ldap üzerinden kullanıcı dogrulaması yapmak istediğimizde, sunucu üzerinden doğrulama yapılıyor.Peki istek yapılan bilgisayar üzerinden doğrulama yapmak istersek.Yani Bir form üzerinden kullanıcı adı ve parolasını istiyorum.Kullanıcı bu bilgileri giriyor ve sorgulama yaptığında , ldap sunucusuna bu istek o bilgisayar daki ip ile yapılmış bir istekmiş gibi olmasını istediğimizde nasıl bir yol izlememiz gerekir.Arka tarafta hangi ip ile bu istek geldi onu öğrenmek istiyorum kısaca.
IP leriniz statik mi dynamic mi.. Dynamic ise zor olur kayit.. Ama baska bir yolla yaparsiniz zaten.. Yaziliminizda bu gibi sorgulari kaydedecek bir veritabani olusturun..Oraya kaydedin istekleri hem de tum detayiyla..
Gayet açıklayıcı ve güzel bir yazı olmuş , elinize sağlık
Teşekkürler..
Cok tesekkürler.
Hemde cok
Rica ederim..
Hocam şu kod olan yerleri kod olarak gösteren bir frame’e koyamadınız mı? Okuması zor maalasef!
Kodlari kopyalarsin olur biter..