ASP.NET Core validate Azure Table Entity already taken - c#

I'm trying to trigger a data validation error back to my view on a lookup to a database back end.
// Perform lookup to see if domain has been taken already
var domainResults = await _context.TenantEntity.SingleOrDefaultAsync(x => x.Office365DomainName == Input.Office365DomainName);
if (domainResults.Office365DomainName == Input.Office365DomainName)
{
// duplicate domain name attempted
user.Office365DomainName = "AlreadyTaken";
return Page();
}
Here is my field:
[Required]
[Display(Name = "Office 365 Domain Name")
public string Office365DomainName { get; set; }
I'd prefer to use a DataAnnotation so I can send back a custom message to the view/user but I'm at a loss on how to build this in.
I've tried changing my property validation to a regex and watching for "AlreadyTaken" as I'm setting this inside my class which contains the same object. My thought was to perform a regex match on something obscure (like a GUID) then have my regex match that GUID for a validation error.
I'm probably over thinking all this and I hope someone has some insight.

As suggested, there was a very easy answer to this:
// Perform lookup to see if domain has been taken already
var domainResult = await _context.TenantEntity.SingleOrDefaultAsync(x => x.Office365DomainName == Input.Office365DomainName);
if (domainResult != null && domainResult.Office365DomainName == Input.Office365DomainName)
{
// duplicate domain name attempted
ModelState.AddModelError("Office365DomainName", "This domain has been registered already.");
return Page();
}
I didn't have to modify my field at all. The following article was a great help: https://exceptionnotfound.net/asp-net-mvc-demystified-modelstate/

Related

Add property to (highly used) existing class vs create new class vs conditional?

I have a site that uses Angular on the frontend and C# on the backend. I have a c# class that obtains data from a database. I have a new field (spanish translation of the value)I want to add, and am not sure which of the following 3 scenarios would be optimal.
This is what the code currently looks like:
public class CustomerIssueManager
{
static public List<GenericRecordDTO> GetCustomerIssues(MyStore db)
{
List<GenericRecordDTO> responseDTO = new List<GenericRecordDTO>();
try
{
var customerIssues = db.CustomerIssues.Where(m => m.IsActive && m.Name.ToLower() != "none").OrderBy(o => o.Name).ToList();
foreach (var c in customerIssues)
{
GenericRecordDTO issue = new GenericRecordDTO();
issue.ID = c.ID;
issue.Value = c.Name;
responseDTO.Add(issue);
}
}
}
}
public class GenericRecordDTO
{
public int ID { get; set; }
public string Value {get; set; } = "";
}
These are the scenarios I am considering:
Scenario 1:
Is there any significant difference between adding the following to GenericRecordDTO:
public string SpanishValue { get; set; } = "";
Ramifications: There are many classes that use GenericRecordDTO that won't need SpanishValue
Scenario 2:
As opposed to creating another class:
public class GenericRecordTransDTO
and adding SpanishValue to it?
Solves the problem from scenario 1
Scenario 3:
As opposed to keeping GenericRecordDTO.cs as is, but in CustomerIssueManager.cs setting a conditional for spanish site:
if(englishSite) { issue.Value = c.Name; }
else { issue.Value = c.SpanishName; }
Not sure if I can check for Spanish site at this point or if I need to do it later? And are there any drawbacks to checking spanish site here vs later in the TypeScript/Angular?
My question would be, why do this on the front end at all? This requires you to handle localization at two levels, while also sending extra data back to the browser that in many cases you do not need.
For me, I would decide the language server side and load content appropriately. If it is a Spanish client requesting the data, then the Name field would be the Spanish name, English user then an English name, etc ... This has the benefit of handling all of the localization on the server side, at least as far as strings are concerned.
However, it is important to understand, localization is more than just text, so you may still need client side formatting for numbers, dates, etc ... For this, I would recommend using a formatting library that takes locale information, then deliver this information from the server via a configuration payload.
As far as detection of the language server side, this could be done quite a few ways. For example:
You could send an explicit header or variable with your requests instructing it which language to use.
You could use the Accept-Language header sent by the browser to decide which language you would like to use.
You could make it a configuration option that the user can set in their profile, and check a database record for the active user to decide which language you would like to use.

Use an "If this already exists in the API" type statement

I have an API that I've created. I've (finally) managed to both GET and POST with it. Now I want to check the POST before it gets submitted.
I have a class with properties (is that the right word? I'm still learning the lingo) of id, name, city, state, and country.
I have a form with a button, and the button has a click event method that looks like this:
protected void submitButton_Click(object sender, EventArgs e)
{
void Add_Site(string n, string ci, string s, string co)
{
using (var client = new HttpClient())
{
site a = new site
{
name = n,
city = ci,
state = s,
country = co
};
var response = client.PostAsJsonAsync("api/site", a).Result;
if (response.IsSuccessStatusCode)
{
Console.Write("Success");
}
else
Console.Write("Error");
}
}
Add_Site(nameText.Text, cityText.Text, stateText.Text, countryText.Text);
}
Now, at this point, it's working as expected. However, I'd like to limit it.
What I want to do is to have it look at the nameText.Text value. Before it runs the POST statement, I want it to look at the other values in the GET of the API and make sure that name doesn't already exist.
While I know that I could probably update the database to make the name field unique, I'd rather do it programatically in C#.
Is this something that's possible within my C# code? If so, what function would I use and how would I get it to return the Site.Name attribute to compare my nameText.Text value?
EDIT:
Here is the code for the POST as requested in one of the comments. Note: This was auto-generated by Visual Studio when I added the controller.
// POST: api/site
[ResponseType(typeof(site))]
public IHttpActionResult Postsite(site site)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.site.Add(site);
db.SaveChanges();
return CreatedAtRoute("DefaultApi", new { id = site.id }, site);
}
I wouldn't have any idea where to even start with adding an "If the name already exists, throw an error," so some kind of walkthrough is plenty.
Here's code for looking in the database if any sites have the provided name (site.Name):
if (db.site.Any(o => o.Name == site.Name))
return BadRequest("Name already exists.");
I eventually determined that I should modify the POST as said in the comments. The solution I used ended up in this question: How to limit POST in web API

ASP.NET Security: single entry of role names

We're building an ASP.NET app, and have a requirement to use the corporate LDAP system (Siteminder) for authentication (upside: no login dialogs). Roles are created in the LDAP tool, and users are assigned to the roles by userland managers (read: the structure has to be easily understood). Currently, all apps that use the system use a dual-entry process whereby the roles identified in the app are hand-entered into the LDAP system and users are assigned, then app functions are assigned to their role mirrors in an app-based control panel. This works, but it bothers me that dual-entry is required.
What I would like to achieve is something where the app queries the LDAP system to get a list of roles that are assigned to the app (which is identified in the LDAP system) and populate the role:function control panel with them. This part seems really straightforward. However, I lose clarity when it comes to figuring out what to put in the Authorize attribute:
[Authorize(Roles = "Admin, Moderator")]
would become... what?
[Authorize(LoadedRoles(r => r.FindAll("some expression that describes the roles that have a particular permission")))]
I'm seriously into blue sky territory here. I read this question, and liked - from an architectural standpoint - the answer that suggested making the permissions the roles. But that might not be acceptable to the userland managers that needed to manage users. On the other hand, this question turns things into non-string resources, but I can't conceive of how to translate that into "roles that have this sort of function included".
Any suggestions?
Update:
Based on the advice of #venerik below, I've made some progress. For the time being, I'm encapsulating everything in the [AuthorizeFunctionAttribute], and will farm the individual pieces out where they belong later. To that end, I created three variables:
private IList<KeyValuePair<long, string>> Roles;
private IList<KeyValuePair<long, string>> Functions;
private IList<RoleFunction> RoleFunctions;
...then put static data in them:
Roles = new ICollection<KeyValuePair<long, string>>();
Roles.Add(KeyValuePair<long, string>(1, "Basic User"));
Roles.Add(KeyValuePair<long, string>(2, "Administrator"));
Functions = new ICollection<KeyValuePair<long, string>>();
Functions.Add(KeyValuePair<long,string>(1,"List Things"));
Functions.Add(KeyValuePair<long,string>(2,"Add Or Edit Things"));
Functions.Add(KeyValuePair<long,string>(3,"Delete Things"));
...and finally bound them together (in a complicated manner that lays the groundwork for the future):
RoleFunctions = new IList<RoleFunction>();
RoleFunctions.Add(
new RoleFunction
{
RoleId = Roles.Where( r => r.Value == "Basic User").FirstOrDefault().Key,
FunctionId = Functions.Where( f => f.Value == "List Things" ).FirstOrDefault().Key,
isAuthorized = true
},
new RoleFunction
{
RoleId = Roles.Where( r => r.Value == "Administrator").FirstOrDefault().Key,
FunctionId = Functions.Where( f => f.Value == "Add or Edit Things" ).FirstOrDefault().Key,
isAuthorized = true
},
// More binding...
);
I feel good about this so far. So I went researching AuthorizeCore to see what I needed to do there. However, per the comment at the bottom of the page, it's not very helpful. I more or less get that at the end, the method needs to return a bool value. And I get that I need to check that one of the User.Roles array fits the permission that's passed in through [AuthorizeFunction("List Things")].
Update (again):
I've got the following code, which seems like it will do what I need (one method needs fleshing out):
/// <summary>An authorization attribute that takes "function name" as a parameter
/// and checks to see if the logged-in user is authorized to use that function.
/// </summary>
public class AuthorizeFunctionAttribute : AuthorizeAttribute
{
private IList<KeyValuePair<long, string>> Roles;
private IList<KeyValuePair<long, string>> Functions;
private IList<RoleFunction> RoleFunctions;
public string Function { get; private set; }
public AuthorizeFunctionAttribute(string FunctionName)
{
Function = FunctionName;
Roles = SetApplicationRoles();
Functions = SetApplicationFunctions();
RoleFunctions = SetRoleFunctions();
}
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
bool userIsAuthorized = false;
foreach (string ur in GetUserRoles(httpContext.Current.Request.Headers["SM_USER"]))
{
long roleId = Roles.Where( sr => sr.Value == ur )
.First().Key;
long functionId = Functions.Where( sf => sf.Value == Function )
.First().Key;
// If any role is authorized for this function, set userIsAuthorized to true.
// DO NOT set userIsAuthorized to false within this loop.
if (RoleFunctions.Where(rf => rf.RoleId == roleId && rf.FunctionId == functionId)
.First().isAuthorized)
{
userIsAuthorized = true;
}
}
return userIsAuthorized;
}
Previously I didn't know enough about the underlying bits of creating a custom attribute to get out of my own way. However, this MSDN article told me what should have been obvious to me in the beginning: build it yourself. So, once I get the GetUserRoles() method put together, I should be underway.
I think you can solve this using a custom AuthorizeAttribute. In a project I worked close to they used that to access Active Directory (as described in this answer).
In your case it would look something like:
public class AuthorizeWithLDAPAttribute(string functionName) : AuthorizeAttribute
{
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
// check LDAP to verify that user has
// a role that's linked to `functionName`
}
}
Next you can use this attribute on your controllers and/or methods:
[AuthorizeWithLDAP("functionName1")]
public class BlogController : Controller
{
....
[AuthorizeWithLDAP("functionName2")]
public ViewResult Index()
{
return View();
}
}
The controller is now only accessible to users whose role are linked to functionName1 and the method is only accessible to users whose role are linked to functionName1 and functionName2

CRM Plugin Error The given key was not present in the dictionary

I developed a CRM plugin that gets triggered when creating a Case entity. The Case has a N:1 relationship with another custom entity. Short story, when a field "new_CaseAssign" from the custom entity is set to 1, then the plugin will generate a Task assigned to Case Owner.
However the problem is that the field "new_CaseAssign" is newly added so most of the custom entity records don't have this field set yet, which causes the plugins to error out with "The given key was not present in the dictionary". If the custom field has value, then it's no problem.
What would be the best way to handle this situation? I tried customEntity.Attributes.Contains("new_CaseAssign") like below codes but no success:
var new_CaseAssign = customEntity.Attributes["new_CaseAssign"];
if ((customEntity.Attributes.Contains("new_CaseAssign")) && (bool)new_CaseAssign)
{
activityEntity.Attributes.Add("ownerid", incident.Attributes["ownerid"]);
}
else
{ //something else
}
Any suggestions is appreciated. Thanks much.
The line:
var new_CaseAssign = customEntity.Attributes["new_CaseAssign"];
is probably causing the exception. You could rewrite the code as:
bool new_CaseAssign = false;
if (customEntity.Attributes.Contains("new_CaseAssign"))
new_CaseAssign = (bool) customEntity.Attributes["new_CaseAssign"];
if (new_CaseAssign) {
activityEntity.Attributes.Add("ownerid", incident.Attributes["ownerid"]);
}
else { //something else
}
So first check contains before trying to access the key.
Loathing's answer is partially correct, in fact you are accessing the attribute before the check if is present or not. Normally I also check if the attribute it's not null before the cast.
The second error is with the field name, you are using late bound style, this means you need to use the logical name instead of the schema name, so the field name is new_caseassign (all lower case) instead of new_CaseAssign.
if ((customEntity.Attributes.Contains("new_caseassign")) && customEntity.Attributes["new_caseassign"] != null)
{
if ((bool)customEntity.Attributes["new_caseassign"])
{
activityEntity.Attributes.Add("ownerid", incident.Attributes["ownerid"]);
}
}
else
{ //something else
}

How to get userID for username in ASMX webservice

I'm trying to store a user's post into an access database through a web method. I want to store the logged-in user's username, user's post, and the post datetime.
So far, I can store an existing user post by hard coding. But I want to store posts by any logged-in users. I was told I need to get userID for username.
Thus, I've found and tried adding the following codes:
//GetUser() returns current user information
MembershipUser user = Membership.GetUser();
//Returns the UserID and converts to a string
string UserID = user.ProviderUserKey.ToString();
When I tried debugging with breakpoints, the first one was okay. But for the second one, VS 2010 said that "object reference not set to an instance of an object." How do I fix it?
VS suggested adding "new," which didn't work. It also suggested to catch NullReferenceException, but I don't know how to use the codes they provided:
public class EHClass
{
void ReadFile(int index)
{
// To run this code, substitute a valid path from your local machine
string path = #"UsersDB_in_App_Data";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}
finally
{
if (file != null)
{
file.Close();
}
}
// Do something with buffer...
}
}
Can you give me suggestions of what I need to do, or an alternative way to go about getting userID for username?
You didn't indicate what type of MembershipUser you have, but the ProviderUserKey is totally dependent on the underlying data store.
For example, the sql membership provider stores this value as a GUID.
If there is a possibility that this property won't contain any useful data, then you need to test it for existence before accessing it:
//Returns the UserID and converts to a string
string UserID;
if ((myObject != null) && (myObject.ProviderUserKey != null)) {
UserId = myObject.ProviderUserKey.ToString();
} else {
UserId = String.Empty;
}
In addition, unless you are using WSE in a straight asmx web service, I don't think that the memebership provider will have any valid data to operate on.
If this is the case, you will probably need to switch to WCF or implement WSE (NOT recommended).
The exception you're getting means that either myObject (the current user) is null or myObject.ProviderUserKey is null. I'd suggest that when you get to the breakpoint after myObject is set you should inspect the value of myObject and see whether it is null.
Depending on what is actually null affects where you look for the problem. If myObject is null then you'll need to look at the code to get the current user, and check whether someone is actually logged in etc. etc. If ProviderUserKey is null, consider whether you need this ID or would be better off with just using the username directly, check whether the membership provider actually provides that property in any meaningful way.
instead you can use this code directly....
string UserID = MembershipUser.ProviderUserKey.ToString();

Categories