I need to let the user query an Active Directory for names in .Net Core.
So I am building an Active Directory Search Web API Service.
I am able to connect with the bind statement.
But I am not able to get any results back with my query although there is no error.
Another programmer sent me some code he uses in other applications. But it uses the DirectoryEntry object which is not available in .Net Core.
So I am trying to use the Novell.Directory.Ldap.NetStandard library.
Here is the code the other developer sent me:
public static List<UserProfileModel> GetADUsers(string alias)
{
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
// Ad path LDAP://ourOrg.gov/CN=Users,DC=ourOrg,DC=gov
DirectoryEntry de2 = new DirectoryEntry(ConfigurationManager.AppSettings["AD_Path"], ConfigurationManager.AppSettings["AD_User"], ConfigurationManager.AppSettings["AD_Password"]);
de2.Path = ConfigurationManager.AppSettings["AD_Path"];
de2.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de2;
deSearch.Filter = "(samaccountname=*" + alias + "*)";
LOGGER.Debug(String.Format("Active Directory Search Filter {0}", deSearch.Filter));
SearchResultCollection results = deSearch.FindAll();
String raw = "";
LOGGER.Debug(String.Format("Active Directory Search Result Counts {0}", results.Count));
if (results.Count > 0)
{
foreach (SearchResult item in results)
{
UserProfileModel userProfileModel = new UserProfileModel();
userProfileModel.Name = GetADProperty("name", item);
userProfileModel.email = GetADProperty("mail", item);
userProfileModel.identity = GetADProperty("userPrincipalName", item);
userProfileModel.first_name = GetADProperty("givenName", item);
userProfileModel.last_name = GetADProperty("sn", item);
users.Add(userProfileModel);
raw = String.Format("{0}/n{1}", raw, userProfileModel.ToString());
}
LOGGER.Debug(String.Format("Active Directory Search Resuts ToString: {0}", raw));
}
}
catch (Exception e)
{
LOGGER.Error("Unable to Query Active Directory", e);
}
return users;
}
I need to translate this into Novell's LDAP library.
Here is my attempt:
[HttpGet]
public async Task<List<UserProfileModel>> GetByName(string alias)
{
int ldapPort = LdapConnection.DEFAULT_PORT;
string ldapHost = "ourOrg.gov";
string loginDn = #"ourOrg\myName";
string password = "myPass";
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
using (var con = new LdapConnection())
{
con.Connect(ldapHost, ldapPort);
con.Bind(loginDn, password);
LdapSearchResults results = con.Search(
"cn=users,dc=ourOrg,dc=gov",
LdapConnection.SCOPE_ONE,
"samaccountname=*",
null,
false);
// NO RESULTS:(
}
return users;
}
catch(Exception ex)
{
throw ex;
}
}
I don't get an error.
But there are 0 results.
I originally had this part:
"samaccountname=*",
like:
"samaccountname={alias}",
but I'm just trying to get back results at this point.
I got this working:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Hrsa.Core.Web.App.Models.ViewModels;
using Novell.Directory.Ldap;
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace Hrsa.Core.Web.App.Controllers.Api
{
[Route("api/[controller]")]
public class ActiveDirectoryController : Controller
{
private readonly AppSettings _appSettings;
public ActiveDirectoryController(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings.Value;
}
[HttpGet]
public async Task<List<UserProfileModel>> GetByName(string alias)
{
int ldapPort = LdapConnection.DEFAULT_PORT;
string ldapHost = _appSettings.HrsaLdapHost; // ourOrgName.gov
string loginDn = _appSettings.AdUser;
string password = _appSettings.AdPassword;
string searchBase = _appSettings.HrsaAdSearchBase;
string searchFilter = $"(samaccountname=*{alias}*)";
string[] attributes = new string[] { "cn", "userPrincipalName", "st", "givenname", "samaccountname",
"description", "telephonenumber", "department", "displayname", "name", "mail", "givenName", "sn" };
List<UserProfileModel> users = new List<UserProfileModel>();
if (alias == null || alias.Trim().Equals(""))
{
return users;
}
try
{
using (var con = new LdapConnection())
{
con.Connect(ldapHost, ldapPort);
con.Bind(loginDn, password);
LdapSearchQueue queue = con.Search(
searchBase,
LdapConnection.SCOPE_SUB,
searchFilter,
attributes,
false,
(LdapSearchQueue)null,
(LdapSearchConstraints)null);
LdapMessage message;
while ((message = queue.getResponse()) != null)
{
if (message is LdapSearchResult)
{
LdapEntry entry = ((LdapSearchResult)message).Entry;
LdapAttributeSet attributeSet = entry.getAttributeSet();
users.Add(new UserProfileModel
{
Cn = attributeSet.getAttribute("cn")?.StringValue,
UserPrincipalName = attributeSet.getAttribute("userPrincipalName")?.StringValue,
St = attributeSet.getAttribute("st")?.StringValue,
Givenname = attributeSet.getAttribute("givenname")?.StringValue,
Samaccountname = attributeSet.getAttribute("samaccountname")?.StringValue,
Description = attributeSet.getAttribute("description")?.StringValue,
Telephonenumber = attributeSet.getAttribute("telephonenumber")?.StringValue,
Department = attributeSet.getAttribute("department")?.StringValue,
Displayname = attributeSet.getAttribute("displayname")?.StringValue,
Name = attributeSet.getAttribute("name")?.StringValue,
Mail = attributeSet.getAttribute("mail")?.StringValue,
GivenName = attributeSet.getAttribute("givenName")?.StringValue,
Sn = attributeSet.getAttribute("sn")?.StringValue
});
}
}
}
return users;
}
catch(Exception ex)
{
throw ex;
}
}
}
}
Related
I have a DTO class using Moshi that's supposed to send and recieve byte arrays[], but it only works when sending them, because when I recieve then I have this exception.
com.squareup.moshi.JsonDataException: Expected BEGIN_ARRAY but was STRING at path $[0].image
But im pretty sure that the type that the API returns is a byte array.
Here's the DTO class and API controller
#JsonClass(generateAdapter = true)
data class LocationImageDTO (
#Json(name="idLocationImage") val idLocationImage: Int,
#Json(name = "idLocation") val idLocation: Int?,
#Json(name="image") val image: ByteArray,
)
//This one is for recieving
public List<clsLocationImage> getList(int idLocation)
{
List<clsLocationImage> list = new List<clsLocationImage>();
clsLocationImage locationImage;
clsMyConnection connection = new clsMyConnection();
SqlCommand command = new SqlCommand
{
CommandText = "SELECT idLocationImage, idLocation, image FROM K0_MAP_LOCATION_IMAGES WHERE idLocation = #idLocation",
Connection = connection.getConnection()
};
command.Parameters.Add("#idLocation", System.Data.SqlDbType.Int).Value = idLocation;
SqlDataReader reader;
try
{
reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
locationImage = new clsLocationImage();
locationImage.idLocationImage = (int)reader["idLocationImage"];
locationImage.idLocation = (int)reader["idLocation"];
locationImage.image = (byte[])reader["image"];
list.Add(locationImage);
}
}
}
catch (SqlException excepcion)
{
throw excepcion;
}
return list;
}
public List<clsLocationImage> getListDAL(int id)
{
return getList(id);
}
}
}
///This one is for sending
public int createLocationImage(clsLocationImage locationImage)
{
int filasAfectadas = 0;
clsMyConnection conexion = new clsMyConnection();
SqlCommand miComando = new SqlCommand
{
CommandText = "INSERT INTO K0_MAP_LOCATION_IMAGES(idLocation, image) VALUES (#idLocation, #image)",
Connection = conexion.getConnection()
};
miComando.Parameters.Add("#idLocation", System.Data.SqlDbType.Int).Value = locationImage.idLocation;
miComando.Parameters.Add("#image", System.Data.SqlDbType.VarBinary).Value = locationImage.image;
try
{
filasAfectadas = miComando.ExecuteNonQuery();
}
catch (SqlException excepcion)
{
throw excepcion;
}
return filasAfectadas;
}
}
}
You need to set an explicit JSON adapter to the Moshi Builder.
class CustomDateAdapter : JsonAdapter<Date>() {
private val dateFormat = SimpleDateFormat(SERVER_FORMAT, Locale.getDefault())
#FromJson
override fun fromJson(reader: JsonReader): Date? {
return try {
val dateAsString = reader.nextString()
synchronized(dateFormat) {
dateFormat.parse(dateAsString)
}
} catch (e: Exception) {
null
}
}
#ToJson
override fun toJson(writer: JsonWriter, value: Date?) {
if (value != null) {
synchronized(dateFormat) {
writer.value(value.toString())
}
}
}
companion object {
const val SERVER_FORMAT = ("yyyy-MM-dd'T'HH:mm") // define your server format here
}
}
And then you add it in the Moshi builder
private val moshiBuilder = Moshi.Builder().add(CustomDateAdapter())
private fun getRetrofit(): Retrofit =
Retrofit.Builder()
.baseUrl(MAPK0_API_BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshiBuilder.build()))
//.client(getUnsafeOkHttpClient())
.build()
}
I suspect this is very simple but I can't find exactly what I need to do.
I am trying to get a password Grant enabled, token endpoint, working with name/password/SomethingExtra
It all works with just name and password. I can see my "Data:SomethingExtra" in acr_values on the server but only in the GetProfileDataAsync override.
So, I can pick up the acr_values in the token generation (GetProfileDataAsync) but I want to "validate" the user with this extra data. How do I test my acr_values at validation?
I suspect there is a method I can override to pick up the "login request" with the extra acr_values so I can decide to return a token or Access Denied much the same was I do with an interactive login using Quickstart web pages.
But what do I override to allow me to authenticate against 3 values?
If you are using password grant then you will have implemented IResourceOwnerPasswordValidator to validate your passwords. You can get the acr values in this class as follows:
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) {
string acrValues = context.Request.Raw.Get("acr_values");
IEnumerable<string> values = acrValues.Trim().Split(new[] { ' ' });
string extraDataAcr = values.FirstOrDefault(x => x.StartsWith("ExtraData:"));
string extraDataValue extraDataAcr?.Substring("ExtraData:".Length);
After a bit of digging...might save someone some time
public class ACustomTokenRequestValidator : ICustomTokenRequestValidator
{
private readonly UserManager<ApplicationUser> _userManager;
public ACustomTokenRequestValidator(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task ValidateAsync(CustomTokenRequestValidationContext context)
{
if (context?.Result?.ValidatedRequest?.GrantType != null &&
context.Result.ValidatedRequest.GrantType.ToUpper() == "PASSWORD") // Only interested in password Grant
{
var acr_values = new Dictionary<string, string>();
string username = string.Empty;
string error = string.Empty;
string Tester = string.Empty;
bool ok = true;
if (context.Result.ValidatedRequest.Raw != null)
{
var reqParamsDict = context.Result.ValidatedRequest.Raw.ToDictionary();
if (reqParamsDict.ContainsKey("acr_values"))
{
var raw = reqParamsDict["acr_values"].Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
acr_values = raw.Select(item => item.Split(':', 2, StringSplitOptions.RemoveEmptyEntries)).ToDictionary(s => s[0], s => s[1]);
}
if (reqParamsDict.ContainsKey("username")) // Should always be there, name/password check would have failed already
{
username = reqParamsDict["username"];
}
else
{
ok = false;
error = "username missing from request";
}
if (ok && acr_values.ContainsKey("ExtraField")) // Could be missing
{
Tester = acr_values["ExtraField"];
}
else
{
ok = false;
error = "ExtraField missing from request";
}
if (ok)
{
if (context.Result.ValidatedRequest.Scopes.Contains("API_Name"))
{
var user = await _userManager.FindByNameAsync(username);
if (user != null)
{
if ( user.ExtraField != Tester )
{
ok = false;
error = "Failed extra test";
}
}
else
{
ok = false;
error = "User not found";
}
}
}
}
if (!ok)
{
context.Result.IsError = true;
context.Result.Error = error;
}
}
}
}
For completeness this is my Postman setup to get it working
[https://i.stack.imgur.com/BtihJ.png][1]
I have an application I am signing in to using SSO office 365 to authenticate the user. I am also calling the azure active directory graph api to pull a list of all users in the organization. I want to stop using the azure active directory graph api (since it is being deprecated as of 2/2019) and move over to microsoft-graph api. If I use microsoft graph to pull users, will I also have to authenticate with diff way (not Azure)?
this is my current auth code in startup file:
public void ConfigureAuth(IAppBuilder app)
{
string strIssuers = ConfigurationManager.AppSettings["validIssuers"];
string[] validIssuers = strIssuers.Split(',');
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"],
ValidIssuers = validIssuers
}
});
}
in graph call:
public async Task<List<User>> GetAdUsers(string tid, string path = "users")
{
var identity = HttpContext.Current.User.Identity as ClaimsIdentity;
string email = identity?.Name;
var selectvalues = "";//(path.ToLower() == "users" ? "$select=*" : "");
List<User> tmpUsers;
string skipToken;
string skipTokenResult;
int skipTokenIndex;
string strAuth = "https://login.microsoftonline.com/" + tid + "/oauth2/v2.0/token";
var client = ConfigurationManager.AppSettings["ida:Audience"];
var secret = ConfigurationManager.AppSettings["clientSecret"];
string clientId = client;
string clientSecret = secret;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = null;
AuthenticationContext _authContext = new AuthenticationContext(strAuth);
Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential creds
= new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
result = await _authContext.AcquireTokenAsync("https://graph.microsoft.com", creds);
var _httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage Res = await _httpClient.GetAsync("https://graph.microsoft.com/v1.0/" + path + "?$top=999" + selectvalues);
if (Res.IsSuccessStatusCode)
{
string strJson = Res.Content.ReadAsStringAsync().Result;
JavaScriptSerializer json = new JavaScriptSerializer();
RootObject rootObj = json.Deserialize<RootObject>(strJson);
List<User> adUsers = rootObj.Value;
var parseRes = JObject.Parse(strJson);
bool stop = false;
while (!stop)
{
try
{
skipTokenResult = parseRes["#odata.nextLink"].Value<string>();
skipTokenIndex = skipTokenResult.IndexOf("skiptoken=");
skipToken = skipTokenResult.Substring(skipTokenIndex + 10, skipTokenResult.Length - skipTokenIndex - 10);
Res = await _httpClient.GetAsync("https://graph.microsoft.com/v1.0/" + path + "?$top=999&$skiptoken=" + skipToken + selectvalues);
if (Res.IsSuccessStatusCode)
{
strJson = Res.Content.ReadAsStringAsync().Result;
rootObj = json.Deserialize<RootObject>(strJson);
tmpUsers = rootObj.Value;
adUsers.AddRange(tmpUsers);
parseRes = JObject.Parse(strJson);
}
else
{
stop = true;
}
}
catch (ArgumentNullException) // no skip token, stop looping !!!!
{
stop = true;
}
}
return adUsers;
}
else
{
// return null;
throw new Exception("GetAdUsers: Graph API failed for path: " + path + ", tid: " + tid + ". Reason: " + Res.ReasonPhrase);
}
}
//UPDATE: I was able to update the code to use SOAP Microsoft Graph API like this:
public GraphServiceClient AuthGraph(string tid, string groupId)
{
try
{
var clientId = ConfigurationManager.AppSettings["ida:Audience"];
var clientSecret = ConfigurationManager.AppSettings["ida:clientSecret"];
var tenantID = tid;
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
//.WithRedirectUri(redirectUri)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
return graphClient;
}
catch (Exception e)
{
throw e;
}
}
public async Task<List<User>> GetAdUsers(string tid, string groupId)
{
try
{
GraphServiceClient graphClient = AuthGraph(tid, groupId);
var graphUsers = await graphClient.Users
.Request()
.GetAsync();
List<User> users = graphUsers.Select(x => new User
{
Id = x.Id,
BusinessPhones = x.BusinessPhones.ToArray(),
DisplayName = x.DisplayName,
GivenName = x.GivenName,
JobTitle = x.JobTitle,
Mail = x.Mail,
MobilePhone = x.MobilePhone,
OfficeLocation = x.OfficeLocation,
PreferredLanguage = x.PreferredLanguage,
Surname = x.Surname,
UserPrincipalName = x.UserPrincipalName
}
).ToList();
if (!string.IsNullOrEmpty(groupId))
{
var membersInGroups = await GetNonSSOUsers(Globals.mghsTid, groupId);
users.AddRange(membersInGroups);
}
return users;
}
catch(Exception ex)
{
_errService.LogError("UserController.Update", tid, ex.HResult, ex.ToString().Substring(0, Math.Min(ex.ToString().Length, Globals.maxErrDescLen)), "getAdUsersService", 1, DateTime.Now.ToString());
throw ex;
}
}
public async Task<List<User>> GetNonSSOUsers(string tid, string groupId)
{
try
{
GraphServiceClient graphClient = AuthGraph(tid, groupId);
var members = await graphClient.Groups[groupId].Members
.Request()
.GetAsync();
List<User> users = new List<User>();
//while (members.NextPageRequest != null && (members = await members.NextPageRequest.GetAsync()).Count > 0)
//{
foreach (var member in members)
{
if (member is Microsoft.Graph.User)
{
var user = (Microsoft.Graph.User)member;
users.Add(new User
{
Id = user.Id,
BusinessPhones = user.BusinessPhones.ToArray(),
DisplayName = user.DisplayName,
GivenName = user.GivenName,
JobTitle = user.JobTitle,
Mail = user.Mail,
MobilePhone = user.MobilePhone,
OfficeLocation = user.OfficeLocation,
PreferredLanguage = user.PreferredLanguage,
Surname = user.Surname,
UserPrincipalName = user.UserPrincipalName
});
}
}
// }
return users;
}
catch (Exception e)
{
throw e;
}
}
The Microsoft Graph API is also protected under Azure AD. So, basically, you just need to add and grant necessary Graph API permissions to your application registered in Azure AD.
After that, you can call the Microsoft Graph API by adding an authorization header.
i want to check if some files exists in a folder on ftp then do specific task
i have the following methos for files check
public static bool CheckFileExistOnFTP(string ServerUri, string FTPUserName, string FTPPassword)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ServerUri);
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
request.Credentials = new NetworkCredential(FTPUserName, FTPPassword);
//request.Method = WebRequestMethods.Ftp.GetFileSize;
// request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
try
{
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
return true;
}
catch (WebException ex)
{
FtpWebResponse response = (FtpWebResponse)ex.Response;
if (response.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
{
return false;
}
}
return true;
}
and i call that method on formload
if (FTPUtility.CheckFileExistOnFTP("ftp://ip address/Requests/", "edexcrawler", "edexcrawler123"))
{
btnUploadRequest.Visible = true;
btnUploadRequest.BackColor = System.Drawing.Color.LightGreen;
btnUploadRequest.ForeColor = System.Drawing.Color.Blue;
}
Based on your other question to get a list of files from ftp, you can check if the file you want to check is in that list:
Var fileNameToCkeck = "myfile.txt";
var utility= new FtpUtility();
utility.UserName = "...";
utility.Password = "...";
utility.Path = "...";
If (utility.ListFiles().Contains(fileNameToCkeck))
{
//The file exists
}
Or if you want to know if that path has any file:
If (utility.ListFiles().Count() > 0)
{
//The folder contains files
}
And here is the code for FtpUtility
public class FtpUtility
{
public string UserName { get; set; }
public string Password { get; set; }
public string Path { get; set; }
public List<string> ListFiles()
{
var request = (FtpWebRequest)WebRequest.Create(Path);
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
request.Credentials = new NetworkCredential(UserName, Password);
List<string> files = new List<string>();
using (var response = (FtpWebResponse)request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
var reader = new StreamReader(responseStream);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (string.IsNullOrWhiteSpace(line) == false)
{
var fileName = line.Split(new[] { ' ', '\t' }).Last();
if (!fileName.StartsWith("."))
files.Add(fileName);
}
}
return files;
}
}
}
}
I want to pass three field in wwwform, which contain two single value and a json array.
Here is my Json:
{
"Fname":"Abc",
"Lname":"Xyz",
"Marks": [{"MarksA":"23","MarksB":"65" },
{"MarksA":"24","MarksB":"56" } ]
}
My current code is
void Start()
{
WWWForm form = new WWWForm();
form.AddField("Fname", "Abc");
form.AddField("Lname", "Xyz");
//passing the array as string
string Mymarks = "[{\"MarksA\":\"23\",\"MarksB\":\"65\" },{\"MarksA\":\"24\",\"MarksB\":\"56\" } ]";
form.AddField("Marks", Mymarks);
WWW www = new WWW("Urltoservice", form);
StartCoroutine("PostRequest", www);
}
IEnumerator PostRequest(WWW www)
{
yield return www;
if (www.error == null)
{
Debug.Log("Session Saved");
}
else
{
Debug.Log("WWW Error: " + www.error);
}
}
But I am getting 500 internal server Error. Please help me.
Try using WWW instead of WWWForm
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class SendData : MonoBehaviour {
void Start()
{
gameObject.GetComponent<Button>().onClick.AddListener(SendOnClick);
}
IEnumerator WaitForWWW(WWW www)
{
yield return www;
string txt = "";
if (string.IsNullOrEmpty(www.error))
txt = www.text; //text of success
else
txt = www.error; //error
GameObject.Find("TextDemo").GetComponent<Text>().text = "--------\n\n" + txt;
}
void SendOnClick()
{
try
{
GameObject.Find("TextDemo").GetComponent<Text>().text = "Starting..";
string ourPostData = "{\"MarksA\":\"23\",\"MarksB\":\"65\" },{\"MarksA\":\"24\",\"MarksB\":\"56\" }";
Dictionary<string,string> headers = new Dictionary<string, string>();
headers.Add("Content-Type", "application/json");
byte[] jData = System.Text.Encoding.ASCII.GetBytes(ourPostData.ToCharArray());
WWW api = new WWW("YOUR URL", jData, headers);
StartCoroutine(WaitForWWW(api));
}
catch (UnityException ex) { Debug.Log(ex.message); }
}
}