I have a class that have three methods that have functionalities on Active Directory. Here is the class:
[Export(typeof(IAuthentication))]
public class Authentication : IAuthentication
{
public bool Authenticate(string domain, string username, string password)
{
try
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domain, string.Empty))
{
return principalContext.ValidateCredentials(
username,
password,
ContextOptions.SimpleBind);
}
}
catch (Exception ex)
{
throw ex;
}
}
public UserPrincipal GetUserDetails(string domain, string username)
{
try
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domain))
{
return UserPrincipal.FindByIdentity(principalContext, username);
}
}
catch (Exception ex)
{
throw ex;
}
}
public PrincipalSearchResult<Principal> SearchUsers(string domain, string firstName, string lastName, string userName)
{
try
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain))
{
UserPrincipal user = new UserPrincipal(ctx);
user.Enabled = true;
user.Name = firstName + "* " + lastName + "*";
user.SamAccountName = userName + "*";
PrincipalSearcher principalSearcher = new PrincipalSearcher();
principalSearcher.QueryFilter = user;
return principalSearcher.FindAll();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
As you see in the class attribute, I'm using this class library as a MEF plugin. In my asp.net mvc 3 application, I call the method like this:
PrincipalSearchResult<Principal> results = _authentication.SearchUsers(
ConfigurationManager.AppSettings["DomainName"],
model.UserSearchCriteria.FirstName,
model.UserSearchCriteria.LastName,
model.UserSearchCriteria.Username);
But after I intend to use the return value of the method, I'm getting Cannot access a disposed object, Object name: 'PrincipalContext' exception. I know I'm disposing the PrincipalContext object but if I do not, the connection to the Active Directory will stay open. I think the design of my class is not correct. How can I make it work in a cool way?
I would imagine that when you eventually enumerate over the instance of PrincipalSearchResult<T>, this deferred operation is trying to access the context, which has since been closed. In this scenario, it is likely better for you to enumerate over the results straight away and return them as domain-specific models. I.e, do the work at the time, and not defer it through returning PrincipalSearchResult<T>.
Related
I need to test this method.
public String getTenantName(String tenantId) {
var tenant = getTenant(tenantId);
if (tenant == null) {
throw new TenantNotFoundException(tenantId);
}
return tenant.getTenantname();
}
but I am having problems with mocking the below loading cache
LoadingCache<String, Tenant> tenantCache = CacheBuilder.newBuilder().maximumSize(1000)
.expireAfterAccess(24, TimeUnit.HOURS).build(new CacheLoader<String, Tenant>() {
#Override
public Tenant load(String tenantId) {
return tenantClient.getTenant(tenantId);
}
});
as this is being called by another private method
private Tenant getTenant(String tenantId) {
try {
if (StringUtils.isBlank(tenantId)) {
return null;
}
return tenantCache.get(tenantId);
} catch (TenantNotFoundException | ExecutionException e) {
logger.error(tenantId, e);
throw new TenantNotFoundException(tenantId);
}
}
I would really appreciate some help here.
I mocked loading cache
#mock
LoadingCache<String, Tenant> tenantCache;
and then in my test function I create a tenant object and return that on tenantCache.get() call.
tenantCache = CacheBuilder.newBuilder().maximumSize(1000)
.expireAfterAccess(24, TimeUnit.HOURS).build(new CacheLoader<String, Tenant>() {
#Override
public Tenant load(String tenantId) {
return tenantClient.getTenant(tenantId);
}
});
Map<String, Tenant> map = new HashMap<String, Tenant>();
map.put("test", tenant);
tenantCache.putAll(map);
also for tenantClient I changed that to return tenant.
return tenantClient.getTenant(id) =>> return tenant;
as tenantClient is calling another API.
So, LoadingCache appears as a variable inside the service but it is implemented as an anonymous class. Therefore we need to mock LoadingCache and use
when(tenantCache.get(anyString())).thenReturn(new Tenant());
I'm getting:
Unable to cast object of type 'System.Net.Http.HttpResponseMessage' to type 'System.Web.Http.IHttpActionResult
Web api2 controller method calls a data access layer that returns an int. It fails on the cast:
return (IHttpActionResult)httpResponse;
Code:
[HttpGet]
[Route("registeruser/{currentDateTime}/{userName}/{userPassword}/{ipAddress}/")]
public IHttpActionResult RegisterUser(DateTime currentDateTime, string userName, string userPassword, string ipAddress)
{
try
{
HttpResponseMessage httpResponse;
int returnValue = 0;
returnValue = dataaccesslayer.RegisterUser(currentDateTime, userName, userPassword, ipAddress);
httpResponse = Request.CreateResponse(HttpStatusCode.OK, returnValue);
return (IHttpActionResult)httpResponse;
}
catch (Exception)
{
throw;
}
}
Data access layer called by web api2 controller method - it returns an int:
public int RegisterUser(DateTime currentDateTime, string userName, string userPassword, string ipAddress)
{
int returnedValue = 0;
try
{
dbFunc.OpenDB();
SqlCommand cmd = new SqlCommand("dbo.RegisterUser", dbFunc.objConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#a_CurrentDateTime", currentDateTime);
cmd.Parameters.AddWithValue("#a_UserName", userName);
cmd.Parameters.AddWithValue("#a_UserPassword", userPassword);
cmd.Parameters.AddWithValue("#a_IpAddress", ipAddress);
cmd.Parameters.Add("#a_UserIdOut", SqlDbType.Int);
cmd.Parameters["#a_UserIdOut"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
returnedValue = (int)cmd.Parameters["#a_UserIdOut"].Value;
return returnedValue;
}
catch (SqlException sqlex)
{
throw sqlex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
// Close the database.
dbFunc.CloseDB();
}
}
If you want to keep the IHttpActionResult try this:
[HttpGet]
[Route("registeruser/{currentDateTime}/{userName}/{userPassword}/{ipAddress}/")]
public IHttpActionResult RegisterUser(DateTime currentDateTime, string userName, string userPassword, string ipAddress)
{
try
{
IHttpActionResult response;
HttpResponseMessage httpResponse;
int returnValue = 0;
returnValue = dataaccesslayer.RegisterUser(currentDateTime, userName, userPassword, ipAddress);
httpResponse = Request.CreateResponse(HttpStatusCode.OK, returnValue);
response = ResponseMessage(httpResponse);
return response;
}
catch (Exception)
{
throw;
}
}
If it is no problem for you to remove it, then do it like this:
[HttpGet]
[Route("registeruser/{currentDateTime}/{userName}/{userPassword}/{ipAddress}/")]
public HttpResponseMessage RegisterUser(DateTime currentDateTime, string userName, string userPassword, string ipAddress)
{
try
{
HttpResponseMessage httpResponse;
int returnValue = 0;
returnValue = dataaccesslayer.RegisterUser(currentDateTime, userName, userPassword, ipAddress);
httpResponse = Request.CreateResponse(HttpStatusCode.OK, returnValue);
return httpResponse;
}
catch (Exception)
{
throw;
}
}
For a longer discussion on between the two, check this link here.
I am using Postman to test an ASP.Net web api 2 application that I created using I created using VS 2017.
It uses ADO.Net to call stored procedures. I tested the stored procedures and they work fine. I created a console app to test the methods and they work fine.
The URL that returns a model object works fine.
http://localhost:56224/api/profileandblog/getactiveuser/2020-03-03/DancinDan/32.211.50.62/1
The URL that returns a boolean does not. I get Error - 404.0 - Not Found
http://localhost:56224/api/profileandblog/validatelogin/2020-03-03/DancinDan/Morewealth1/32.211.50.62
Here is the dataaccesslayer.cs in my Models folder:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using GbngWebApi2.ADO_Utilities;
namespace GbngWebApi2.Models
{
public class DataAccessLayer
{
DatabaseFunctions dbFunc = new DatabaseFunctions();
public bool ValidateLogin(DateTime currentDateTime, string userName, string userPassword, string ipAddress)
{
bool returnedStatus = false;
try
{
dbFunc.OpenDB();
SqlCommand cmd = new SqlCommand("dbo.ValidateLogin", dbFunc.objConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#a_CurrentDateTime", currentDateTime);
cmd.Parameters.AddWithValue("#a_UserName", userName);
cmd.Parameters.AddWithValue("#a_UserPassword", userPassword);
cmd.Parameters.AddWithValue("#a_IpAddress", ipAddress);
// Set the OUT parameter.
cmd.Parameters.Add("#a_PasswordStatusSwitchOut", SqlDbType.Bit);
cmd.Parameters["#a_PasswordStatusSwitchOut"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
// Get the value from the OUT parameter.
// Cast to Boolean.
returnedStatus = (bool)cmd.Parameters["#a_PasswordStatusSwitchOut"].Value;
return returnedStatus;
}
catch (SqlException sqlex)
{
throw sqlex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
dbFunc.CloseDB();
}
}
public User GetActiveUser(DateTime currentDateTime, string userName, string ipAddress, int userId)
{
User user = new User();
SqlDataReader userDataReader = null;
try
{
dbFunc.OpenDB();
SqlCommand cmd = new SqlCommand("dbo.GetActiveUser", dbFunc.objConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#a_CurrentDateTime", currentDateTime);
cmd.Parameters.AddWithValue("#a_UserName", userName);
cmd.Parameters.AddWithValue("#a_IpAddress", ipAddress);
cmd.Parameters.AddWithValue("#a_UserId", userId);
userDataReader = cmd.ExecuteReader();
while (userDataReader.Read())
{
user.UserId = Convert.ToInt32(userDataReader["UserId"]);
user.UserName = userDataReader["UserName"].ToString();
user.ActiveSwitch = Convert.ToInt32(userDataReader["ActiveSwitch"]);
user.ApiAccessSwitch = Convert.ToInt32(userDataReader["ApiAccessSwitch"]);
user.AdminSwitch = Convert.ToInt32(userDataReader["AdminSwitch"]);
user.BlogAuthorSwitch = Convert.ToInt32(userDataReader["BlogAuthorSwitch"]);
user.BlogUserName = userDataReader["BlogUserName"].ToString();
user.IpAddress = userDataReader["IpAddress"].ToString();
user.IpAddressUsedForRegisteringCount = Convert.ToInt32(userDataReader["IpAddressUsedForRegisteringCount"]);
user.LoginCount = Convert.ToInt32(userDataReader["LoginCount"]);
user.ModifiedCount = Convert.ToInt32(userDataReader["ModifiedCount"]);
user.SuggestionCount = Convert.ToInt32(userDataReader["SuggestionCount"]);
user.SelectedForPublicViewSwitch = Convert.ToInt32(userDataReader["SelectedForPublicViewSwitch"]);
user.ModifiedDateTime = Convert.ToDateTime(userDataReader["ModifiedDateTime"]);
user.CreatedDateTime = Convert.ToDateTime(userDataReader["CreatedDateTime"]);
}
return user;
}
catch (SqlException sqlex)
{
throw sqlex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (userDataReader != null)
{
userDataReader.Close();
}
dbFunc.CloseDB();
}
}
}
}
Here is the WebApi2Controller:
using System;
using System.Web.Http;
using GbngWebApi2.Models;
namespace GbngWebApi2.Controllers
{
[RoutePrefix("api/profileandblog")]
public class WebApi2Controller : ApiController
{
DataAccessLayer dataaccesslayer = new DataAccessLayer();
[HttpGet]
[Route("validatelogin/{currentDateTime}/{userName}/{userPassword}/{ipAddress}")]
public bool ValidateLogin(DateTime currentDateTime, string userName, string userPassword, string ipAddress)
{
try
{
// Returns a boolean indicator of success or failure.
return dataaccesslayer.ValidateLogin(currentDateTime, userName, userPassword, ipAddress);
}
catch (Exception)
{
throw;
}
}
[HttpGet]
[Route("getactiveuser/{currentDateTime}/{userName}/{ipAddress}/{userId}")]
public User GetActiveUser(DateTime currentDateTime, string userName, string ipAddress, int userId)
{
try
{
// Returns the active "user" from the database.
return dataaccesslayer.GetActiveUser(currentDateTime, userName, ipAddress, userId);
}
catch (Exception)
{
throw;
}
}
}
}
From Nkosi: Add a slash at the end and it will work /32.211.50.62/
I am trying to execute custom asyncCodeActivity in UIPath. Added the package, passing all data, however UIPath just hangs when it reaches custom activity and does not throw any exceptions/or stops. I tried to create Class Library using CodeActivity and AsyncCodeActivity - my activity should make several APICalls but I get result it just stops when it reaches my custom activity and does not go to the next one. Is there any example how to create async custom activity for UIPath? My class library worked ok when I tried to test it outside of UIpath. Will appreciate any help.
My class library using CodeActivity:
public class AddInvoice : CodeActivity
{
[Category("Input")]
[RequiredArgument]
public InArgument<string> PickupZip { get; set; }
[Category("Output")]
[RequiredArgument]
public OutArgument<String> Output { get; set; }
public async Task<string> ApiTest(CodeActivityContext context)
{
try
{
var origin = await GoogleAPIWrapper.GetAddressByZip(PickupZip.Get(context));
string PickupAddress;
string DeliveryAddress;
var inv = new IList();
if (origin.StatusId >= 0)
{
invoice.PickupCity = origin.Locality;
invoice.PickupState = origin.AdminLevel1;
}
else
{
invoice.PickupCity = null;
invoice.PickupState = null;
}
var tkn = token.Get(context);
var client = new HttpClient();
HttpClientHandler handler = new HttpClientHandler();
client = new HttpClient(handler, false);
client.BaseAddress = new Uri("http://test.test.com/");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + tkn);
StringContent content = new StringContent(JsonConvert.SerializeObject(inv), Encoding.UTF8, "application/json");
var response = await client.PostAsync("api/insert/", content);
var resultContent = response.StatusCode;
Output.Set(context, resultContent.ToString());
}
catch (Exception e)
{
Output.Set(context, e.ToString());
}
return "ok";
}
protected override void Execute(CodeActivityContext context)
{
try
{
string result = ApiTest(context).GetAwaiter().GetResult();
}
catch (Exception e)
{
Output.Set(context, e.ToString());
}
}
public class IList
{
public string PickupState { get; set; }
public string PickupCity { get; set; }
}
}
Classes that derive from CodeActivity are synchronous by default. Since UiPath is based on Windows Workflow, deriving from an AsyncCodeActivity class should work.
You didn't ask explicitly for it, but since you're essentially calling a web service, have a look at the Web Activities package, the HTTP Request in particular. This also comes with JSON deserialization. You can find more information about web service integration here, for example (disclaimer: I am the author).
I subclassed org.apache.commons.beanutils.BeanUtilsBean so that it ignores NULL properties:
public class NullAwareBeanUtilsBean extends BeanUtilsBean {
Logger log = Logger.getLogger(this.getClass());
#Override
public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException {
if (value == null) {
log.debug("skipping property " + name);
return;
}
super.copyProperty(dest, name, value);
}
}
My user class has a Collection of Stats:
public class User implements Serializable{
private Integer id;
private String username;
private Set<Stat> stats = new HashSet<Stat>();
}
If I do this it works fine:
public void updateUser(User user) {
User dbUser = userDao.read(user.getId());
dbUser.setUsername(user.getUsername());
log.debug("about to save" + dbUser);
userDao.update(dbUser);
}
But if I use copyProperties() it thinks that the Set of Stats is empty and tries to delete it:
public void updateUser(User user) {
User dbUser = userDao.read(user.getId());
//the problem here is that hibernate does not like the copyProperties method...
try {
NullAwareBeanUtilsBean nullAwareBeanUtilsBean = new NullAwareBeanUtilsBean();
nullAwareBeanUtilsBean.copyProperties(dbUser, user);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//dbUser = (User) userDao.getCurrentSession().merge(dbUser);
//log.debug("stats size=" + dbUser.getStats().size());
log.debug("about to save" + dbUser);
userDao.update(dbUser); }
I have tried using Hibernate.initialize(), as well as referring to the Set before and after using BeanUtils to init the Set (which it does if called before), but it doesn't matter because it is empty after (even if I re-init)... I also tried merging it into the Session but that didn't work either. Any thoughts? I'm thinking it might have to do with Hibernate creating a proxy object and BeanUtils somehow messing that up.
I think that the problem can be that "user" has an empty set (not null object), so copyProperties is copying the empty Set of "user" into the existing Set with values of "dbUser" (so, when you save the dbUser, you are cleaning the Set). If you want to prevent also the copy of and empty set, you can change your method to:
public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException {
if (value == null) {
log.debug("skipping property " + name);
return;
}
if ((value instanceof Set) && ((Set) value).isEmpty()) {
log.debug("skipping empty Set" + name);
return;
}
super.copyProperty(dest, name, value);
}