I recently upgraded RestSharp to version 107.3.0. I had to modify my request some, but the Web API gets the request and returns, but it hangs there waiting for the response...
private async Task<bool> AuthenticateUser(string username, string password)
{
var encryption = new Encryption64();
var encrypt = encryption.Encrypt(password, _key);
var client = new RestClient(UserSettings.URL);
var uri = $"users/authenticate/{username}/";
var pass = new PasswordDTO
{
Password = encrypt
};
var request = new RestRequest(uri)
.AddJsonBody(pass);
//var json = JsonConvert.SerializeObject(pass);
//request.AddParameter("application/json; charset=utf-8", json, ParameterType.RequestBody);
var response = await client.PostAsync<bool>(request);
return response;
}
The line await client.PostAsync<bool>(request) never completes. No errors in the Debug window either. This worked before I upgraded. What am I doing wrong?
I am not sure if this is entirely correct... but I fiddled with it until I got it working.
private bool AuthenticateUser(string username, string password)
{
using (var client = new RestClient(UserSettings.URL))
{
var encryption = new Encryption64();
var encrypt = encryption.Encrypt(password, _key);
var uri = $"users/authenticate/{username}/";
var pass = new PasswordDTO
{
Password = encrypt
};
var request = new RestRequest(uri)
.AddJsonBody(pass);
var response = client.PostAsync<bool>(request).Result;
return response;
}
}
UPDATE
private async Task<bool> AuthenticateUser(string username, string password)
{
using (var client = new RestClient(UserSettings.URL))
{
var encryption = new Encryption64();
var encrypt = encryption.Encrypt(password, _key);
var uri = $"users/authenticate/{username}/";
var pass = new PasswordDTO
{
Password = encrypt
};
var request = new RestRequest(uri)
.AddJsonBody(pass);
var response = await client.PostAsync<bool>(request);
return response;
}
}
public async Task<bool> AuthenticateUserAsync([FromUri] string username, [FromBody] PasswordDTO pass)
{
Log.Logger.ForContext<UserController>().Information("{User} is Logging In", username);
using (var context = new DatabaseContext())
{
var user = await context.bma_users
.AsNoTracking()
.FirstOrDefaultAsync(p => p.username == username);
if (user is null)
return false;
return (user.password == pass.Password);
}
}
I still welcome any ideas for improvement.
Related
I am working on an asp.net web application where I have to create users directly in Tableau Server using Rest API and C#. The users are local users and not AD or SAML users.
I have coded the part however, I am getting Unauthorised as the result. Below is my code.
public static string site_id { get; set; }
public static string token { get; set; }
static void Main(string[] args)
{
CallWebAPIAsync().Wait();
}
static async Task CallWebAPIAsync()
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
client.BaseAddress = new Uri("https://serverurl.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
SignIn(client);
Users(client, site_id, token);
CreateUser(client, site_id, token);
}
Console.Read();
}
public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
public CustomXmlMediaTypeFormatter()
{
UseXmlSerializer = true;
WriterSettings.OmitXmlDeclaration = false;
}
}
public static tableauCredentialsType SignIn(HttpClient client)
{
var name = "Administrator";
var password = "Password";
var site = string.Empty;
var tableauCredentialsType = new tsRequest()
{
Item = new tableauCredentialsType
{
name = name,
password = password,
site = new siteType() { contentUrl = site }
}
};
var httpContent = new ObjectContent<tsRequest>(tableauCredentialsType, new CustomXmlMediaTypeFormatter());
var httpResponseMessage = client.PostAsync("/api/3.2/auth/signin", httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to login to the server", site, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((tableauCredentialsType)responseLogin.Items[0]);
site_id = resultTableauCredentialsType.site.id;
token = resultTableauCredentialsType.token;
return resultTableauCredentialsType;
}
public static userType CreateUser(HttpClient client, string siteid, string auth_token)
{
var name = "user#domain.com";
var userType = new tsRequest()
{
Item = new userType
{
name = name,
siteRole = siteRoleType.ExplorerCanPublish,
authSetting = siteUserAuthSettingType.ServerDefault
}
};
var httpContent = new ObjectContent<tsRequest>(userType, new CustomXmlMediaTypeFormatter());
client.DefaultRequestHeaders.Add("x-tableau-auth", auth_token);
var uri = string.Format("/api/3.2/sites/{0}/users", siteid);
var httpResponseMessage = client.PostAsync(uri, httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to create user in the server", siteid, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((userType)responseLogin.Items[0]);
return resultTableauCredentialsType;
}
I was able to successfully login into the server and was also able to provide the x-tableau-auth to my client headers.
The code is working fine except the post sync method in Create user method. Once the code executes, it throws UnAuthorized error as the response from the server. My user is the administrator in Tableau Server and when I query the workbooks and users in Tableau Server with the same x-tableau-auth, it's working fine.
I am not good in API programming and need some help in solving out this issue. Any help is highly appreciated.
I have created a POST Action which should return a Complex type as shown below as part of the HTTPContent:
[HttpPost]
[Route("users/verifyLoginCredentials")]
public IHttpActionResult VerifyLoginCredentials([FromBody]Dictionary<string,string> loginCredentials) // Purely here for testing purouses
{
Models.User user = new Models.User();
string username = (loginCredentials.ContainsKey("userLogin"))? loginCredentials["userLogin"]: "";
string password = (loginCredentials.ContainsKey("password"))? loginCredentials["password"] : "";
var verificationResult = Models.User.VerifyLoginCredentials(username, password, out user);
HttpContent responseContent = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(user));
if (verificationResult)
{
return Ok(responseContent);
}
return BadRequest("Login Failed");
}
And then within my client application I want to read the Complex type which has been serialised to a Json object so the obvious would be to de-serialise it, but where is it(the User Object)?
public static async Task<bool> VerifyCredentials(string userLogin, string password)
{
var value = new Dictionary<string, string>
{
{ "userLogin", userLogin },
{ "password", password }
};
var test = Newtonsoft.Json.JsonConvert.SerializeObject(value);
var content = new StringContent(test, Encoding.UTF8, "application/json");
var result = await WebApiHelper.Client.PostAsync("users/verifyLoginCredentials", content);
string resultContent = await result.Content.ReadAsStringAsync();
var user = (User)Newtonsoft.Json.JsonConvert.DeserializeObject(resultContent);
if (result.IsSuccessStatusCode)
{
return true;
}
return false;
}
On the client side
Instead of doing this in two steps:
var result = await WebApiHelper.Client.PostAsync("users/verifyLoginCredentials", content);
/*1: read as string*/string resultContent = await result.Content.ReadAsStringAsync();
/*2: deserialize*/ var user = (User)JsonConvert.DeserializeObject(resultContent);
You can do this in one step:
var result = await WebApiHelper.Client.PostAsync("users/verifyLoginCredentials", content);
/*1: read as User*/User resultContent = await result.Content.ReadAsAsync<User>();
This, however:
if (result.IsSuccessStatusCode)
{
return true;
}
Has to be checked before attempting to deserialize, otherwise you would be trying to deserialize "Login Failed" as a User object - which of course isn't valid. So:
public static async Task<bool> VerifyCredentials(string userLogin, string password)
{
var value = new Dictionary<string, string>
{
{ "userLogin", userLogin },
{ "password", password }
};
var test = Newtonsoft.Json.JsonConvert.SerializeObject(value);
var content = new StringContent(test, Encoding.UTF8, "application/json");
var result = await WebApiHelper.Client.PostAsync("users/verifyLoginCredentials", content);
if (result.IsSuccessStatusCode)
{
return true;
}
User resultContent = await result.Content.ReadAsAsync<User>();
return false;
}
I am not sure whether you win anything by that deserialization though
On the server side
This code here:
var verificationResult = Models.User.VerifyLoginCredentials(username, password, out user);
HttpContent responseContent = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(user));
if (verificationResult)
{
return Ok(responseContent);
}
Is overly complex. Web API already JSON-serializes the model by default, you don't have to do all of that. Instead:
var verificationResult = Models.User.VerifyLoginCredentials(username, password, out user);
if (verificationResult)
{
return Ok(user);
}
Also, you should be using a proper Data Transfer Object (DTO), instead of receiving a Dictionary that you cannot have the Framework validate for you, but that's another problem.
I have written a code to fetch details from active directory as a GET call based on user id and password. I am passing user id and password in the url like -http://localhost:1234/api/User/IsAuthorized/UserID=1234;password=qwerty
but this is an unsafe technique. Can anybody give me a solution to pass these values in the body and use it as a POST call instead of a get call
my code goes like-
[Route("IsAuthorized/UserID={userName};password={password}")]
[AllowAnonymous]
public IHttpActionResult GetIsAuthorized(string userName,string password)
{
HttpResponseMessage response = null;
string errorMessage = null;
bool hasError = false;
bool isValid;
UserDetails detail = null;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "ABC"))
{
isValid = pc.ValidateCredentials(userName, password);
string token = null;
if (isValid)
{
detail = IsAuthenticated("abc", userName, password, out errorMessage, out hasError);
}
if (hasError)
{
detail = new UserDetails(isValid, userName, null, null, null, errorMessage, null);
}
else
{
if (detail != null)
{
token = CreateToken(userName);
detail = new UserDetails(isValid, userName, detail.AssociateName, detail.Mobile, detail.Email, null, token);
}
else
detail = new UserDetails(isValid, userName, null, null, null, "unknown username or bad password", null);
}
return Ok(detail);
}
}
Why are you even doing it like this? You must be calling you web api from a client. Why are you not using the HttpClient to pass your credentials to your api. Something like this:
public async Task<TResult> PostAsync<TResult, TInput>(string uriString, TInput payload = null) where TInput : class
{
var uri = new Uri(uriString);
using (var client = GetHttpClient())
{
var jsonContent = JsonConvert.SerializeObject(payload, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(jsonContent, Encoding.UTF8, "application/json"));
if (response.StatusCode != HttpStatusCode.OK)
{
//Log.Error(response.ReasonPhrase);
return default(TResult);
}
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResult>(json);
}
}
private HttpClient GetHttpClient()
{
var client = new HttpClient();
var username = //get username
var password = // get password
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
So, because HttpClient is "better," I need to convert WebClient to HttpClient, but it just doesn't work as expected. The following function uses WebClient and works like a charm.
private static void Authenticate()
{
Console.WriteLine("Authenticating . . .");
var clientId = ConfigurationManager.AppSettings["AuthNClientId"];
var uri = ConfigurationManager.AppSettings["AuthNUri"];
var userName = ConfigurationManager.AppSettings["AuthNUserName"];
var password = ConfigurationManager.AppSettings["AuthNPassword"];
var client = new WebClient();
string formData = $"client_id={clientId}&grant_type=password&username={userName}&password={password}";
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
var response = client.UploadString($"{uri}", formData);
dynamic authResult = JsonConvert.DeserializeObject(response);
_accessToken = authResult.access_token;
if (_accessToken == null)
{
throw new ApplicationException("Unable to authenticate. Check your configuration file <appSettings>.");
}
Console.WriteLine("Authenticated.");
}
This code, on the other hand, returns a BadRequest response code.
static async Task<string> GetAuthenticationToken()
{
string token = string.Empty;
var clientId = ConfigurationManager.AppSettings["AuthNClientId"];
var uri = ConfigurationManager.AppSettings["AuthNUri"];
var userName = ConfigurationManager.AppSettings["AuthNUserName"];
var password = ConfigurationManager.AppSettings["AuthNPassword"];
client.BaseAddress = new Uri("https://myurl.com/oauth2/token");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded");
var path = $"client_id={clientId}&grant_type=password&username={userName}&password={password}";
HttpResponseMessage response = await client.GetAsync($"https://myurl.com/oauth2/token?{path}");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("success");
token = await response.Content.ReadAsStringAsync();
}
else { Console.WriteLine($"failure: {response.StatusCode}"); }
return token;
}
You can see that I've tried it a couple of ways, including setting the client BaseAddress as well as just try to pass the url into the GetAsync method.
Anyone see what I'm doing wrong here?
UploadString is a POST method in the first example. In the second example a GET method is being done.
static async Task<string> GetAuthenticationTokenAsync() {
string token = string.Empty;
var clientId = ConfigurationManager.AppSettings["AuthNClientId"];
var uri = ConfigurationManager.AppSettings["AuthNUri"];
var userName = ConfigurationManager.AppSettings["AuthNUserName"];
var password = ConfigurationManager.AppSettings["AuthNPassword"];
var client = new HttpClient();
client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
var nameValueCollection = new Distionary<string, string>() {
{ "client_id", clientId },
{ "grant_type", "password" },
{ "username", userName },
{ "password", password },
};
var content = new FormUrlEncodedContent(nameValueCollection);
var response = await client.PostAsync("", content);
if (response.IsSuccessStatusCode) {
Console.WriteLine("success");
var json = await response.Content.ReadAsStringAsync();
dynamic authResult = JsonConvert.DeserializeObject(json);
token = authResult.access_token;
}
else { Console.WriteLine($"failure: {response.StatusCode}"); }
return token;
}
public async static Task<RootObject> GetWeather(string username, string password)
{
var http = new HttpClient();
var response = await http.GetAsync(postURI);
var result = await response.Content.ReadAsStringAsync();
var serializer = new DataContractJsonSerializer(typeof(RootObject));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
var datax = (RootObject)serializer.ReadObject(ms);
return datax;
}
I have the necessary models ready and I made the function call with some hard-coded data to test but its not working.