ASP.Net Core : get members of Active Directory group - asp.net

I'm wondering how I could get a list of members of an AD group.
Checking if an entered password of a user is correct works perfectly fine. For this I'm using Novell's Ldap.NetStandard:
private bool IsUserValid(string userName,string userPassword)
{
try{
using (var connection = new LdapConnection { SecureSocketLayer = false })
{
connection.Connect("test.local", LdapConnection.DEFAULT_PORT);
connection.Bind(userDn, userPassword);
if (connection.Bound)
{
return true;
}
}
}
catch (LdapException ex)
{
Console.WriteLine(ex.Massage);
}
return false;
}
What I want now is something like this:
bool isUserInGroup("testUser","testGroup");
The problem is I can't get my method working:
public bool IsUserMemberOfGroup(string userName,string groupName)
{
var ldapConn = GetConnection();
var searchBase = "";
var filter = $"(&(objectClass=group)(cn={groupName}))";
var search = ldapConn.Search(searchBase, LdapConnection.SCOPE_BASE, filter, null, false);
while (search.hasMore())
{
var nextEntry = search.next();
if (nextEntry.DN == userName)
return true;
}
return false;
}
What ever I'm doing, I'm not getting back any value from my Ldap.Search()...

Now there is an implementation of System.DirectoryServices.AccountManagement for .NET Core 2. It is available via nuget.
With this package you are able to things like that:
using (var principalContext = new PrincipalContext(ContextType.Domain, "YOUR AD DOMAIN"))
{
var domainUsers = new List<string>();
var userPrinciple = new UserPrincipal(principalContext);
// Performe search for Domain users
using (var searchResult = new PrincipalSearcher(userPrinciple))
{
foreach (var domainUser in searchResult.FindAll())
{
if (domainUser.DisplayName != null)
{
domainUsers.Add(domainUser.DisplayName);
}
}
}
}
This performs a search for the user in your domain.Nearly the same is possible for searching your group. The way I used to search my AD (description in my question) is now obsolet:
Checking if an entered password of a user is correct works perfectly
fine. For this I'm using Novell's Ldap.NetStandard:

How about:
HttpContext.User.IsInRole("nameOfYourAdGroup");
(namespace System.Security.Claims)

Related

vulnerability from security team in forget password controller in asp .net

I have a controller form application and the security team they said there is a vulnerability you can put any user_id fom postman inside the controller like this
ForgotPassword/user_id
how I can remove this vulnerability check the code below:
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult ForgotPassword(string emailId)
{
var helper = new Helper.Helper();
List<SqlParameter> args = new List<SqlParameter>();
args.Add(new SqlParameter("#Pin_email_id", emailId));
var req_resp = new Dictionary<string, object>();
try
{
using (DataSet dataset = helper.ExecuteSqlQuery("Web_Forgot_Password", args))
{
if (dataset != null && dataset.Tables.Count > 0 && dataset.Tables[0].Rows.Count > 0)
{
if (dataset.Tables[0].Rows[0]["Status"].ToString() == "Success")
{
req_resp["status"] = true;
req_resp["message"] = dataset.Tables[0].Rows[0]["Description"].ToString();
req_resp["code"] = dataset.Tables[0].Rows[0]["Code"].ToString();
string password = dataset.Tables[0].Rows[0]["user_password"].ToString();
SendForgotMail(emailId, dataset.Tables[0].Rows[0]["user_name"].ToString(), helper.Decrypt(password), dataset.Tables[0].Rows[0]["employee"].ToString());
return Json(req_resp);
}
else
{
req_resp["status"] = false;
req_resp["message"] = dataset.Tables[0].Rows[0]["Description"].ToString();
req_resp["code"] = dataset.Tables[0].Rows[0]["Code"].ToString();
return Json(req_resp);
}
}
else
{
req_resp["status"] = false;
req_resp["message"] = "Request Failed";
req_resp["code"] = "1005";
return Json(req_resp);
}
}
}
catch
{
var response = new
{
status = false,
message = "Request failed",
code = "1005"
};
return Json(response);
}
}
Well normally you store only password hashes in your database, which are not decryptable. Watching helper.Decrypt(password) in your code and sending the original password as a plain text in email is something painful. Normally I would just send a password reset link which can be used only once.
I checked the SqlParemater docs, it is added as a String value the way you use it, so it is not SQL injectable. Without the exact SQL I cannot tell much. I think they meant that it is SQL injectable, but then they should send evidence at least.

how we can return a status code for the serialized JSON object using Newtonsoft.net

I have this Action method which act as an API end point inside our ASP.NET MVC-5, where it search for a username and return the username Phone number and Department from Active Directory (we are serializing the object using Newtonsoft.net):-
public ActionResult UsersInfo2()
{
DomainContext result = new DomainContext();
try
{
// create LDAP connection object
DirectoryEntry myLdapConnection = createDirectoryEntry();
string ADServerName = System.Web.Configuration.WebConfigurationManager.AppSettings["ADServerName"];
string ADusername = System.Web.Configuration.WebConfigurationManager.AppSettings["ADUserName"];
string ADpassword = System.Web.Configuration.WebConfigurationManager.AppSettings["ADPassword"];
using (var context = new DirectoryEntry("LDAP://mydomain.com:389/DC=mydomain,DC=com", ADusername, ADpassword))
using (var search = new DirectorySearcher(context))
{
// create search object which operates on LDAP connection object
// and set search object to only find the user specified
// DirectorySearcher search = new DirectorySearcher(myLdapConnection);
// search.PropertiesToLoad.Add("telephoneNumber");
search.Filter = "(&(objectClass=user)(sAMAccountName=test.test))";
SearchResult r = search.FindOne();
ResultPropertyCollection fields = r.Properties;
foreach (String ldapField in fields.PropertyNames)
{
// cycle through objects in each field e.g. group membership
// (for many fields there will only be one object such as name)
string temp;
// foreach (Object myCollection in fields[ldapField])
// {
// temp = String.Format("{0,-20} : {1}",
// ldapField, myCollection.ToString());
if (ldapField.ToLower() == "telephonenumber")
{
foreach (Object myCollection in fields[ldapField])
{
result.Telephone = myCollection.ToString();
}
}
else if (ldapField.ToLower() == "department")
{
foreach (Object myCollection in fields[ldapField])
{
result.Department = myCollection.ToString();
}
}
// }
}
string output = JsonConvert.SerializeObject(result);
return Json(output,JsonRequestBehavior.AllowGet);
}
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" + e.ToString());
}
return View(result);
}
now the return JSON will be as follow:-
"\"DisplayName\":null,\"Telephone\":\"123123\",\"Department\":\"IT\",\"Name\":null,\"SamAccountName\":null,\"DistinguishedName\":null,\"UserPrincipalName\":null}"
but in our case we need to return a status code beside the return json data. for example inccase there is an exception we need to return an error code,also if we are able to get the user's info we need to pass succes code 200, and so on.. so how we can achieve this?
you can try something like this
var statusCode=200;
string output = JsonConvert.SerializeObject( new { result = result, StatusCode = statusCode);
but nobody usually do this. When users call API they can check status code that HTTP Client returns, using code like this
var response = await client.GetAsync(api);
//or
var response = await client.PutAsJsonAsync(api, data);
var statusCode = response.StatusCode.ToString();
//or usually
if (response.IsSuccessStatusCode) {...}
else {...}

WebAPI call hangs when return a large amount of data

I have a web api call that I recently added to my app. I returns a complete list of all countries, states and cities in the app (currently 486 rows) I perform this call when all of the reference data for my application loads (I have a base loading page and call the function in my startup class to load all the data there). The challenge is that the call to get all my countries.... hangs and eventually I get "The operation was canceled" error. If I modify my stored procedure that selects the data from the database on the server to only return say 20 rows, it runs fine. Any suggestions?
Below is the code from the startup class:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace GBarScene
{
class StartUpClass
{
public event GeneralDataLoad BaseDataLoadComplete;
public async Task<GBSStartUpEventArgs> ProcessStartup()
{
GBSStartUpEventArgs lobj_EventArgs;
lobj_EventArgs = new GBSStartUpEventArgs();
App.InStartUpDataLoad = true;
try
{
if (!App.IsGeolocationEnabled)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoLocationServicesMessage");
}
else if (!App.InternetIsAvailable)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("NoInternetConnectionFound");
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = false;
}
else
{
Debug.WriteLine("Process StartUp");
await Task.Delay(500);
//Reset values
ViewModelObjects.DayOfWeek.DataLoadProcessed = false;
ViewModelObjects.Languages.DataLoadProcessed = false;
if (await ViewModelObjects.DayOfWeek.LoadData() == false)
// //try it once more
await ViewModelObjects.DayOfWeek.LoadData();
Debug.WriteLine("GBar After DayofWeek Load");
await ViewModelObjects.Languages.LoadData();
Debug.WriteLine("GBar After Languages Load");
if ((ge_AppMode)ViewModelObjects.AppSettings.AppMode == ge_AppMode.CitySelected)
{
//We need to reload the NearbyCities and set the selected one
await ViewModelObjects.NearbyCities.LoadData();
}
Debug.WriteLine("Before load of coutries");
await ViewModelObjects.CountryStateCity.LoadData();
Debug.WriteLine("After load of coutries");
Debug.WriteLine("Count: " + ViewModelObjects.CountryStateCity.CountryItems_ForList.Count.ToString());
ViewModelObjects.NumberOfResults.LoadData();
ViewModelObjects.Perspectives.LoadData();
ViewModelObjects.SearchRadiuses.LoadData();
ViewModelObjects.UseMetric.LoadData();
while (!ViewModelObjects.DayOfWeek.DataLoadProcessed && !ViewModelObjects.Languages.DataLoadProcessed && !App.IsGeolocationEnabled)
{
await Task.Delay(100);
}
if (App.BaseDataLoadError)
{
lobj_EventArgs.ErrorOccurred = true;
lobj_EventArgs.ShowRetry = true;
lobj_EventArgs.ShowWebSite = true;
lobj_EventArgs.ErrorMessage = resourcestrings.GetValue("ErrorLoadingReferenceData");
}
}
Debug.WriteLine("StartUp Process Ended");
BaseDataLoadComplete(this, lobj_EventArgs);
}
catch (Exception ex)
{
App.ProcessException(ex);
}
App.InStartUpDataLoad = false;
return lobj_EventArgs;
}
}
}
This is the helper class that makes all the WebAPI calls:
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace GBarScene
{
public class WebAPICaller: IDisposable
{
HttpClient iobj_HTTPClient = null;
public void Dispose()
{
if (iobj_HTTPClient != null)
iobj_HTTPClient.Dispose();
}
public async Task<string> HTTPGetWebServiceAsync(string ps_URI)
{
string ls_Response = "";
string ls_JSONData = "";
string ls_Prefix = "";
try
{
iobj_HTTPClient = await GetClient();
switch (Device.RuntimePlatform)
{
case Device.Android:
ls_Prefix = App.APIStandardPrefix;
break;
//case Device.Android:
// ls_Prefix = App.APISecurePrefix;
// break;
//case Device.Windows:
//case Device.WinPhone:
// ls_Prefix = App.APISecurePrefix;
// break;
default:
ls_Prefix = App.APISecurePrefix;
break;
}
Debug.WriteLine("before api call");
iobj_HTTPClient.BaseAddress = new Uri(ls_Prefix);
ls_JSONData = await iobj_HTTPClient.GetStringAsync(ps_URI);
Debug.WriteLine("after api call");
ls_Response = System.Net.WebUtility.HtmlDecode(ls_JSONData);
}
catch (Exception ex)
{
Debug.WriteLine("api call error");
App.ProcessException(ex);
}
return ls_Response;
}
public async Task<bool> HTTPPostWebService(string ps_URI, object pobj_BodyObject)
{
HttpResponseMessage lobj_HTTPResponse = null;
bool lb_Response = false;
HttpContent lobj_Content = null;
try
{
if (iobj_HTTPClient != null)
iobj_HTTPClient = await GetClient();
iobj_HTTPClient.BaseAddress = new Uri(App.APISecurePrefix);
lobj_Content = new StringContent(JsonConvert.SerializeObject(pobj_BodyObject == null ? "" : pobj_BodyObject));
lobj_Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
lobj_HTTPResponse = await iobj_HTTPClient.PostAsync(ps_URI, lobj_Content);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Exception lobj_Exception = new Exception(lobj_HTTPResponse.ToString());
lobj_Exception.Source = "HTTPGetWebService for: " + ps_URI;
App.ProcessException(lobj_Exception);
}
else
{
lb_Response = true;
}
}
catch (Exception ex)
{
App.ProcessException(ex);
}
finally
{
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
//Debug.WriteLine("WebAPICaller-CallWebService-1: Done");
}
return lb_Response;
}
private async Task<HttpClient> GetClient()
{
HttpClient lobj_HTTPClient = null;
if (lobj_HTTPClient == null)
{
lobj_HTTPClient = new HttpClient();
lobj_HTTPClient.DefaultRequestHeaders.Add("Accept", "application/json");
lobj_HTTPClient.MaxResponseContentBufferSize = 2147483647;
lobj_HTTPClient.Timeout = new TimeSpan(0,0,0,0,60000);
}
return lobj_HTTPClient;
}
}
}
Sorry I forget to include the method in the CountryStateCity view model that calls the webapi helper class.
public async Task<bool> LoadData()
{
string ls_Response = "";
string ls_WorkURI = "";
WebAPICaller lobj_WebAPICaller = null;
bool lb_DataLoaded = false;
try
{
IsDataLoaded = false;
//Debug.WriteLine("City Data Load");
lobj_WebAPICaller = new WebAPICaller();
ls_WorkURI = ic_CoutryStateCityAPIUrl.Replace("{Language}", "EN");
ls_Response = await lobj_WebAPICaller.HTTPGetWebServiceAsync(ls_WorkURI);
if (ls_Response.Trim().Length == 0)
{
AddErrorEntry();
}
else
{
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>();
StateItems_ForList = new ObservableCollection<GBSState_ForList>();
CityItems_ForList = new ObservableCollection<GBSCity_ForList>();
iobj_CountryStateCity = JsonConvert.DeserializeObject<ObservableCollection<GBSCountryStateCity>>(ls_Response);
//Now load the display lists
CountryItems_ForList = new ObservableCollection<GBSCountry_ForList>(
(from lobj_Country in iobj_CountryStateCity
select new GBSCountry_ForList()
{
ID = lobj_Country.Country_Code,
Value = lobj_Country.Country_Name_Text
}).Distinct().ToList());
CountryItems_ForList.Insert(0, new GBSCountry_ForList
{
ID = "XX",
Value = "Base Value"
});
lb_DataLoaded = true;
}
}
catch (Exception ex)
{
AddErrorEntry();
App.ProcessException(ex);
}
finally
{
IsDataLoaded = true;
if (lobj_WebAPICaller != null)
lobj_WebAPICaller.Dispose();
}
return lb_DataLoaded;
}
So after much time, I believe I figured out what the problem is. The problem started to manifest itself again with smaller amounts of data and I could not figure out why. The problem appeared. The issue appears to be the IP address I was using. (I was using the IP address of the actual laptop I was hosting both the App and WebAPIs on.) It appears you have to use one of the other network adaptors for the emulator to have this work reliably.
Here are the steps I used to resolved this:
I launched my Windows 10 mobile emulator.
Click on the >> (Tools) icon in the tool bar of the emulator.
Click on the Network tab of the Additional Tools window.
Look in the list for the network adaptor labeled Desktop Adaptor #1 and copy the IP address.
Edit the Applicationhost.config file in the folder of the WebAPI project.
Find the entry in the file for site name="XXXXX" where XXXXX is the name of the Visual Studio project you are hosting your WebAPIs in.
Within the section of the entry for your WebAPI project, add a binding for the IP address you copied from in step 4. It should look something like this:
<binding protocol="http" bindingInformation="*:56952:169.254.69.220" />
Where 56952 is the port my IIS Express is hosting the WebAPIs on and 169.254.69.220 is the IP address I copied from step 4. After adding this, I was able to connect to locally hosted WebAPIs in IIS Express.
Hope this helps.

Active Directory exception asp .net

public bool IsUserGroupMember(string user, string unit)
{
bool member = false;
try
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
string[] groups = unit.Split(',');
foreach (string word in groups)
{
GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, word);
if (grp != null)
{
foreach (Principal p in grp.GetMembers(true))
{
if (p.SamAccountName == user)
{
member = true;
grp.Dispose();
ctx.Dispose();
return member;
}
}
}
else
{
grp.Dispose();
ctx.Dispose();
return member;
}
}
}
catch (COMException)
{
return member;
}
return member;
}
I'm using the method above to find if a user is member of a group in Active Directory, recursively. It works well..although sometimes I get a weird exception.
Specified method is not supported.
foreach (Principal p in grp.GetMembers(true)) is red (sorry I can't upload a picture of the exception). The weirdest thing is that it seems to be thrown randomly, and if I refresh the page it works well..
I tried to find a solution on the Internet but no happy news for now..
You should do this the other way around: get the user and then the authorization groups that this user is a member of - this call (.GetAuthorizationGroups on the UserPrincipal) already is searching the groups recursively for you!
public bool IsUserGroupMember(string user, string unit)
{
bool isMember = false;
try
{
// put the PrincipalContext in a using(..) block - then it's
// automatically, safely and properly disposed of at the end
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// get the user
UserPrincipal up = UserPrincipal.FindByIdentity(ctx, user);
if(up != null)
{
// get the authorization groups for the user
// this call is *RECURSIVELY* enumerating all groups
// that this user is a member of
var authGroups = up.GetAuthorizationGroups();
// now that you have the groups - just determine if the user
// is a member of the group you're looking for......
}
}
}
catch (COMException comEx)
{
isMember = false;
}
return isMember;
}
I finally ended up with a solution!
I just had to add my domain name, as follow:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "MyDomain");
It solved the problem right away!
And about the slowness..I used cookies, following this link.

WCF Forms Authentication authorization using ASP.NET roles = Access Denied?

I have a basic WPF application where the client writes to a database. I'm using IIS on a server 2012 machine to host the web service. I'm trying to implement Forms authentication, and I have all that working (passing a username and password in the xaml.cs from the client which authenticates my ASP.NET user which works. I then want to implement ASP.NET roles authorization for different commands (Submit Request, Remove Request, etc). The method we're supposed to use is "[PrincipalPermission(SecurityAction.Demand, Role = "Allowed")]"
In theory this should just use the credentials passed in the client (which I've confirmed works) when I try to hit the buttons and it should check if the user I passed is in the role, and if so it allows and if not it denies. However, whether or not the user is in the role it still says "Access is Denied".
Any thoughts?
using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Security.Permissions;
using RequestRepository;
using System.Threading;
using System.Web;
namespace RequestServiceLibrary
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RequestService : IRequestService
{
private List<Request> requests = new List<Request>();
private RequestLibraryEntities context = new RequestLibraryEntities();
[PrincipalPermission(SecurityAction.Demand, Role = "Allowed")]
public string SubmitRequest(Request req)
{
Thread.CurrentPrincipal = HttpContext.Current.User;
if (context.Requests.Count() == 0)
populateRequests();
req.Id = Guid.NewGuid().ToString();
req.TimeSubmitted = DateTime.Now;
requests.Add(req);
addRequest(req);
return req.Id;
}
[PrincipalPermission(SecurityAction.Demand, Role = "Allowed")]
public bool UpdateRequest(Request req)
{
Thread.CurrentPrincipal = HttpContext.Current.User;
bool returnval = false;
try
{
var getobject = requests.Find(x => x.Id.Equals(req.Id));
if (getobject != null) //checks to make sure the object isn't empty
{
getobject.Username = req.Username;
getobject.Password = req.Password;
getobject.RequestedResource = req.RequestedResource;
getobject.TimeSubmitted = req.TimeSubmitted;
}
//Find the request object in the database
var Id = Guid.Parse(req.Id);
var rl = context.Requests.Find(Id);
//Update that object with the values from req
rl.Username = req.Username;
rl.Password = req.Password;
rl.RequestedResource = req.RequestedResource;
rl.TimeTransmitted = req.TimeSubmitted;
context.SaveChanges();
returnval = true;
return returnval;
}
catch (Exception) { return returnval; }
}
public List<Request> GetRequests()
{
populateRequests();
return requests;
}
[PrincipalPermission(SecurityAction.Demand, Role = "Disallowed")]
public bool RemoveRequest(string id)
{
bool rval = false;
try
{
Request req = requests.Find(x => x.Id.Equals(id));
requests.Remove(req);
rval = delRequest(req);
return rval;
}
catch (Exception)
{
return false;
}
}
private void populateRequests()
{
requests = new List<Request>();
var rl = context.Requests.ToList();
foreach (var r in rl)
{
requests.Add(new Request()
{
Id = r.Id.ToString(),
Password = r.Password,
RequestedResource = r.RequestedResource,
TimeSubmitted = r.TimeTransmitted,
Username = r.Username
});
}
}
private void addRequest(Request req)
{
try
{
var r = context.Requests.Create();
r.Id = Guid.Parse(req.Id);
r.Username = req.Username;
r.Password = req.Password;
r.RequestedResource = req.RequestedResource;
r.TimeTransmitted = req.TimeSubmitted;
context.Requests.Add(r);
context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Console.WriteLine("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
}
private bool delRequest(Request req)
{
Guid Id = Guid.Parse(req.Id);
var r = context.Requests.Create();
r.Id = Id;
var rl = context.Requests.Find(Id);
try
{
context.Requests.Remove(rl);
context.SaveChanges();
return true;
}
catch (Exception) { return false; }
}
}
}
In order to be able to use PrincipalPermissionAttribute in this way, you need to first set Thread.CurrentPrincipal to a Principal with the appropriate roles ("Allowed" in this case).
For example you could use ClientRoleProvider to do this, or simply create the Principal manually (possibly using roles you retrieve from the web service).

Resources