C# enable to associate a value to an attribute of an interface - c#

Hi I'm new in development and I'm trying to understand interfaces. Well I understood when to use them but I'm trying to associate a value to an attribute of an interface and I see an error.
public class Consumer : IUser
{
public string Role { get; set; }
public string Consumer { get; set }
}
..
public class Admin : IUser
{
public string Role { get; set; }
public string Admin { get; set; }
}
..
public interface IUser
{
string Role { get; set; }
}
here the code i'm trying to test but I see "Use of unassigned local variable".
IUser user;
string role = "role test";
user.Role = role;
That's fine but I can't create a new instance of IUser because it's an interface. The only solution I found is to create a new Consumer and a new Admin but isn't what I'm trying to do.
My plan is to retrieve the Role attribute from an XML, associate it with the Role of the IUser interface and return it. It'll be a responsibility of another part to check if the role is of an User or Admin
string xmlRole = reader.GetAttribute("Role");
var user = new IUser(); //error
//var user = new Customer(); works but not my plan
//var user = new Admin(); works but not my plan
user.Role = xmlRole;

First of all, interface is an contract, which means it only tells what methods class (which implements that interface) should have.
While coding you can say, for example that given parameter should be of type IUser (Which basically means, that you can assign to it any class as long as it implements IUser).
Example:
interface IUser
{
bool Register();
}
class Administrator : IUser {}
class User : IUser {}
Then, you have method:
public bool HandlerRegister(IUser user)
{
//You can use method here, because you know that class used as parameter will
//implement that method
return user.Register();
}
In your case, when deserializing some XML you need to somehow say that given deserialized object is of type IUser.
But if I were you, I would just create class which represents Role and then, add that class as contract inside interface :)
Then, it would be preaty easy to assign it to all classes that implements yours interface.

Related

ASP.NET Core 3.1 Web Api authorization on model property level

I have a web api with basic jwt authentication and role based authorization. Now I want to restrict certain fields from being edited by users that are in the role user, because the route based authorization is not enough.
class Account {
public int Id {get; set;}
public string Email {get; set;}
public string Password {get; set;}
public bool Enabled {get; set;} // <- this field should only be editable by an admin or manager
public int RoleId {get; set;} // <- this field should only be editable by an admin
}
When the user is in the role user he is only allowed to change his email address and his password, but only for his account. When he is in the role manager he should be able to edit the fields email, password and enabled but only for accounts that are in the user role. An admin can edit every field from every user.
Is there anything that would solve my problem, for example something like this:
class Account {
public int Id {get; set;}
public string Email {get; set;}
public string Password {get; set;}
[Authorize(Roles = "Admin,Manager")]
public bool Enabled {get; set;} // <- this field should only be editable by an admin or manager
[Authorize(Roles = "Admin")]
public int RoleId {get; set;} // <- this field should only be editable by an admin
}
More infos about my project:
- ASP.NET Core 3.1
- I use Entity Framework Core with a Postgres database
- For authentication I use basic jwt bearer authentication
So, I think you has incorrect understanding of Authtorize working.
This attribute uses for Controllers. You can create multiple controllers and set for each method different ROLES to specify what Roles can call this method.
It's not correct to specify it on Dto (Data Transfer Objects) classes.
But you can make some interesting solution with 2 controllers and inheritance.
//Account dto for edit
class AccountEditDto {
public int Id {get; set;}
public string Email {get; set;}
public string Password {get; set;}
}
//Controller to edit account
[Route("all/account_controller")]
public class AccountController : Controller
{
public ActionResult EditAccount(AccountEditDto accountDto)
{
//do something
}
}
Then for create manager roles setup something like this :
//Account dto for edit
class AccountManagerEditDto : AccountEditDto {
public bool Enabled {get; set;}
}
//Controller admin to edit account
[Area("Manager")]
[Route("manager/account_controller")]
public class AccountManagerController : AccountController
{
[Authorize(Roles = "Manager")]
public ActionResult EditAccount(AccountManagerEditDto accountDto)
{
//Do something
}
}
Then for create admin roles setup something like this :
//Account dto for edit
class AccountAdminEditDto : AccountManagerEditDto {
public int RoleId {get; set;}
}
//Controller admin to edit account
[Area("Admin")]
[Route("admin/account_controller")]
public class AccountAdminController : AccountController
{
[Authorize(Roles = "Admin")]
public ActionResult EditAccount(AccountAdminEditDto accountDtp)
{
//Do something
}
}
Then you can use than pattern of URL for call controller methods:
http://localhost/{role}/accont_controller/edit
jwt is token based, meaning that every user has it's unique token which he uses in order to access your system.
The easiest way for you to achieve this is to decode the token and check for the roles, thus enabling you to set role specific action for users.
You will not be able to do this via attributes because jwt does not support them.
There is a good guide to walk you through it as it will be too long for an answer here:
Role based JWT tokens
It requires basic understanding of how tokens work and the guide provides brief explanation.
Although I'm not really Fan of this answer but you might you need this answer duo to a internal policy on your workplace.
Disclaimer:: this question is treated as business rule validation because there is many ways technically to sperate them in a way that make since.
For me I like the First answer which made a very good segregation of the Apis....
Although this is the think you are looking for you MUST keep them in one api: :
[System.AttributeUsage(System.AttributeTargets.Property, Inherited = true)]
public class AuthorizeEdit : ValidationAttribute
{
private readonly string _role;
private IHttpContextAccessor _httpContextAccessor;
public AuthorizeEdit(string role) : base()
{
_role = role;
}
public override bool IsValid(object value)
{
return _httpContextAccessor.HttpContext.User?.IsInRole(_role) ?? false;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
_httpContextAccessor = (IHttpContextAccessor)validationContext
.GetService(typeof(IHttpContextAccessor));
return base.IsValid(value, validationContext);
}
}
and you can use it like this :
class Account {
public int Id {get; set;}
public string Email {get; set;}
public string Password {get; set;}
public bool Enabled {get; set;} // <- this field should only be editable by an admin or manager
[Authorize(Roles = "Admin")]
public int RoleId {get; set;} // <- this field should only be editable by an admin
}
THE DOWNSIDE : Any one whos not in role in the validator will not be able to change any failed what so ever
My recommendation: separate your Apis like the first answer suggest then add this filter to the admin api
Note you can change the filter to take as roles as u want
Unfortunately, this is not possible as it isn't something built-in to ASP.NET Core 3.1.
However, why not carry out your logic in the handler?
You can either create multiple endpoints for users (which I wouldn't recommend) or as per common convention, use 1 route and just validate the data based on the user's role before processing the data.
Get the current account, check to see what has changed and if the user has changed a property which they should have no permission to change, return HTTP 403 Forbidden without further processing their request.
If they have the right role for the action, continue as normal.
You can inherit AuthorizeAttribute and write your own like so:
public class myAuthorizationAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
// do any stuff here
// it will be invoked when the decorated method is called
if (CheckAuthorization(actionContext))
return true; // authorized
else
return false; // not authorized
}
private bool CheckAuthorization(HttpActionContext actionContext)
{
bool isAuthorized = ... // determine here if user is authorized
if (!isAuthorized) return false;
var controller = (myController)actionContext.ControllerContext;
// add those boolean properties to myController
controller.isEnabledReadOnly = ... // determine here if user has the role
controller.isRoleIdReadOnly = ... // determine here if user has the role
return true;
}
}
In the function CheckAuthorization you can check roles available and then set flags in your code to decide whether the related fields should be allowed written to or not.
It can be simply used on any method like:
[myAuthorization]
public HttpResponseMessage Post(string id)
{
// ... your code goes here
response = new HttpResponseMessage(HttpStatusCode.OK); // return OK status
return response;
}
Since you've added the properties isEnabledReadOnly and isRoleIdReadOnly to your controller, you can directly access them in any (Get, Post, ...) method.
Note: You can use the actionContext to access Request, Response, ControllerContext etc (see here).
More information about the internals (how this works) can be found here.
You can do in an action. You sould code like this.
public class AccountController : Controller
{
[HttpPost("EditProfile")]
public async Task<IActionResult> EditProfile(User user)
{
var fields = new List<string>();
fields.Add("Id");
fields.Add("Email");
fields.Add("Password");
if (User.IsInRole("Admin")){
fields.Add("RoleId");
fields.Add("Enabled ");
}else if(User.IsInRole("Manager"))){
fields.Add("Enabled ");
}
var updateUser = context.Entry(user);
foreach (var field in fields)
{
updateUser.Property(field).IsModified = true;
}
await context.SaveChangesAsync();
}
}
Well, I would personally suggest using #TemaTre's answer but since you ask. I could mention another possibility other than using a custom serializer. Which is to use a custom model binder.
You need a few basic steps
Define a custom attribute
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class AuthorizePropertyAttribute : Attribute
{
readonly string roles;
public AuthorizePropertyAttribute(string roles)
{
this.roles = roles;
}
public string Roles
{
get { return roles; }
}
}
Annotate your model with that attribute
public class Account
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[AuthorizeProperty("Admin,Manager")]
public bool Enabled { get; set; } // <- this field should only be editable by an admin or manager
[AuthorizeProperty("Admin")]
public int RoleId { get; set; } // <- this field should only be editable by an admin
}
Define a custom ModelBinder
public class AuthorizedModelBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
using var reader = new StreamReader(bindingContext.HttpContext.Request.Body);
var body = await reader.ReadToEndAsync();
var jObject = JsonConvert.DeserializeObject(body, bindingContext.ModelType); // get the posted object
var modelType = bindingContext.ModelType;
var newObject = Activator.CreateInstance(modelType); // this is for demo purpose you can get it in a way you want (like reading it from db)
var properties = modelType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
var user = bindingContext.HttpContext.User; // this is also for demo purpose only
foreach (var prop in properties)
{
var auth = prop
.GetCustomAttributes(typeof(AuthorizePropertyAttribute), true)
.OfType<AuthorizePropertyAttribute>().FirstOrDefault(); // check the property has that auth attribute
if (auth == null)
{
prop.SetValue(newObject, prop.GetValue(jObject)); // if not assign that property
}
else
{
var isInRole = auth.Roles.Split(",", StringSplitOptions.RemoveEmptyEntries).Any(user.IsInRole);
if (isInRole) // this guy has access
{
prop.SetValue(newObject, prop.GetValue(jObject));
}
}
}
}
}
And finally, if you want every Account object to go through this model binder you can annotate your class like this :
[ModelBinder(typeof(AuthorizedModelBinder))]
public class Account ...
Or you can specify it on the Action you want to use like this :
public IActionResult Sup([ModelBinder(typeof(AuthorizedModelBinder))]Account ACC)...

Return only a subset of properties of an object from an API

Say I have a database in which I am storing user details of this structure:
public class User
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
}
I have a data access layer that works with this that contains methods such as GetById() and returns me a User object.
But then say I have an API which needs to return a users details, but not sensitive parts such as the PasswordHash. I can get the User from the database but then I need to strip out certain fields. What is the "correct" way to do this?
I've thought of a few ways to deal with this most of which involve splitting the User class into a BaseClass with non sensitive data and a derived class that contains the properties I would want kept secret, and then converting or mapping the object to the BaseClass before returning it, however this feels clunky and dirty.
It feels like this should be a relatively common scenario, so am I missing an easy way to handle it? I'm working with ASP.Net core and MongoDB specifically, but I guess this is more of a general question.
It seems for my purposes the neatest solution is something like this:
Split the User class into a base class and derived class, and add a constructor to copy the required fields:
public class User
{
public User() { }
public User(UserDetails user)
{
this.UserId = user.UserId;
this.Name = user.Name;
this.Email = user.Email;
}
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserDetails : User
{
public string PasswordHash { get; set; }
}
The data access class would return a UserDetails object which could then be converted before returning:
UserDetails userDetails = _dataAccess.GetUser();
User userToReturn = new User(userDetails);
Could also be done using AutoMapper as Daniel suggested instead of the constructor method. Don't love doing this hence why I asked the question but this seems to be the neatest solution and requires the least duplication.
There are two ways to do this:
Use the same class and only populate the properties that you want to send. The problem with this is that value types will have the default value (int properties will be sent as 0, when that may not be accurate).
Use a different class for the data you want to send to the client. This is basically what Daniel is getting at in the comments - you have a different model that is "viewed" by the client.
The second option is most common. If you're using Linq, you can map the values with Select():
users.Select(u => new UserModel { Name = u.Name, Email = u.Email });
A base type will not work the way you hope. If you cast a derived type to it's parent type and serialize it, it still serializes the properties of the derived type.
Take this for example:
public class UserBase {
public string Name { get; set; }
public string Email { get; set; }
}
public class User : UserBase {
public string UserId { get; set; }
public string PasswordHash { get; set; }
}
var user = new User() {
UserId = "Secret",
PasswordHash = "Secret",
Name = "Me",
Email = "something"
};
var serialized = JsonConvert.SerializeObject((UserBase) user);
Notice that cast while serializing. Even so, the result is:
{
"UserId": "Secret",
"PasswordHash": "Secret",
"Name": "Me",
"Email": "something"
}
It still serialized the properties from the User type even though it was casted to UserBase.
If you want ignore the property just add ignore annotation in you model like this, it will skip the property when model is serializing.
[JsonIgnore]
public string PasswordHash { get; set; }
if you want ignore at runtime(that means dynamically).there is build function avilable in Newtonsoft.Json
public class User
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
//FYI ShouldSerialize_PROPERTY_NAME_HERE()
public bool ShouldSerializePasswordHash()
{
// use the condtion when it will be serlized
return (PasswordHash != this);
}
}
It is called "conditional property serialization" and the documentation can be found here. hope this helps
The problem is that you're viewing this wrong. An API, even if it's working directly with a particular database entity, is not dealing with entities. There's a separation of concerns issue at play here. Your API is dealing with a representation of your user entity. The entity class itself is a function of your database. It has stuff on it that only matters to the database, and importantly, stuff on it that does not matter to your API. Trying to have one class that can satisfy multiple different applications is folly, and will only lead to brittle code with nested dependencies.
More to the point, how are you going to interact with this API? Namely, if your API exposes your User entity directly, then any code that consumes this API either must take a dependency on your data layer so it can access User or it must implement its own class representing a User and hope that it matches up with what the API actually wants.
Now imagine the alternative. You create a "common" class library that will be shared between your API and any client. In that library, you define something like UserResource. Your API binds to/from UserResource only, and maps that back and forth to User. Now, you have completely segregated your data layer. Clients only know about UserResource and the only thing that touches your data layer is your API. And, of course, now you can limit what information on User is exposed to clients of your API, simply by how you build UserResource. Better still, if your application needs should change, User can change without spiraling out as an API conflict for each consuming client. You simply fixup your API, and clients go on unawares. If you do need to make a breaking change, you can do something like create a UserResource2 class, along with a new version of your API. You cannot create a User2 without causing a whole new table to be created, which would then spiral out into conflicts in Identity.
Long and short, the right way to go with APIs is to always use a separate DTO class, or even multiple DTO classes. An API should never consume an entity class directly, or you're in for nothing but pain down the line.

How to use different attributes in inherited objects in C#

I am working on a web application where different user roles should have different security rights (view, edit). In order to implement this, I have a custom attribute [ReadOnlyAuthorize("Supervisor, Administrator, SysAdministrator")].
When a field is marked with this attribute and the user does not have one of these roles, my editor templates mark the field as readonly and disabled.
The problem is that by using the same model in create, edit and search operations, I get the same functionality and this is not what I want. This looks really bad but it is a temporary solution.
Is there a way to override these annotations besides by using new and defining new attributes?
I hope this description makes sense to you, these are parts of my classes:
[MetadataType(typeof(AssetMetadata))]
public partial class DTAsset
{
}
public class AssetMetadata
{
[ReadOnlyAuthorize("SysAdministrator")]
[DisplayName("Asset ID:")]
public string AssetID { get; set; }
}
public class AssetSearchViewModel : AssetMetadata
{
// removed [ReadOnlyAuthorize("SysAdministrator")]
[DisplayName("Asset ID:")]
public string AssetID { get; set; }
}

WCF handling objects

I have a class which is placed in a class library:
public class UserProfile
{
public int UserId { get; set; }
public string UserName { get; set; }
}
Then I have a repository class:
public class Repository
{
public List<UserProfile> GetUsers()
{
using (var context = new DBContext())
{
List<UserProfile> list = context.UserProfiles.ToList();
return list;
}
}
}
business logic class:
public class BusinessLogic
{
public List<UserProfile> GetUserProfiles()
{
Repository repo = new Repository();
List<UserProfile> list = repo.GetUsers().ToList();
return list;
}
}
and finaly WCF:
public interface IService1
{
[OperationContract]
List<UserProfile> GetUserProfiles();
}
public class Service1 : IService1
{
public List<UserProfile> GetUserProfiles()
{
BusinessLogic.BusinessLogic bl = new BusinessLogic.BusinessLogic();
List<UserProfile> list = bl.GetUserProfiles().ToList();
return list;
}
}
Whenever I try to get user profiles from wcf, it returns empty list.
However, if I skip wcf and get List<UserProfile> straight from businesslogic, it works perfectly fine.
I tried to debug. Results: when inside wcf it gets list from businesslogic, it's already empty. But as I said earlier, business logic works perfectly fine (returns necessary information).
There were similar posts but none of them did help me.
How can I make my WCF return a list filled with necessary information?
P.S. I do not want to add a copy of my class UserProfile into wcf with [DataContract] flag
Your object must either be serializable or decorated with that DataContract attribute. Your return type from WCF must also be decorated with the DataContract attribute, and the member containing your List must be marked with the DataMember attribute. This is required by WCF's DataContractSerializer in order to properly serialize the data and return it to the consumer. Converting a class for transmission over the wire requires serialization. There isn't a practical way to get around this with WCF.
Your list is empty because your UserProfile class cannot be serialized.
EDIT:
I just saw you are simply returning a list, which is already serializable, so if you just make your UserProfile class serializable or decorate it with the appropriate DataContract/DataMember classes, it will start working fine.

Creating a Global Object?

I'm trying to achieve the following:
When a user has been successfully authenticated, I need to create a Global user object which I can access from different Forms (or classes).
I was looking through the Data Sources available in VS, and saw there's a "Object" option which may be suitable for what I'm trying to achieve. The trouble is, I have no idea how it works.
Can anyone point me in the right direction?
Thanks.
Assuming that this is a Windows Forms application, you can create a User class that is stored in a static ApplicationState class.
Steps:
1) Create your user class to hold information about the user:
public class User
{
public string Login { get; set; }
//.. other properties
}
2) Create your ApplicationState class:
public static class ApplicationState
{
public static User CurrentUser { get; set; }
}
3) In your login process, create a new version of the user class and assign it to the ApplicationState.CurrentUser property:
public void CompleteLogin(string sLogin)
{
User user = new User();
user.Login = sLogin;
ApplicationState.CurrentUser = user;
}
4) You can now use ApplicationState.CurrentUser just about anywhere in your project.
It's called a Singleton, and it's a Bad Thing (especially when its state can be mutated). Investigate Dependency Injection (DI) instead. There's even a .NET-specific book on it.
Per request, here's a simple (probably oversimplified) example of doing DI manually. A DI Container library (also known as an Inversion of Control or IoC Container library) can simplify the process of "wiring everything up" in the composition root, and usually also provides lifetime management and other features.
// Composition root of your application
void Main()
{
// Only instance of user we will ever create
var user = new User();
var instance = new MyClass(user);
}
public interface IUser
{
string Name { get; set; }
}
public class User: IUser
{
public string Name { get; set; }
}
public class MyClass
{
public MyClass(IUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
_user = user;
}
private readonly IUser _user;
}
You could use or create a public List making sure that you only add the authenticated users to the List once they have been validated you could even do this via encapsulating field access, create a List or string[] property what you are asking you probably would want to create a class level Property.

Categories

Resources