I'm developing 3rd party API connector bridge in class library NOT in ASP.NET.
User Levels
API has 3 user levels, lets say:
UserGoer
UserDoer
UserMaker
Service Restriction
Each API operation can work with one or multiple user level roles. For example, lets assume operations and reachable user levels as follows;
JokerService (reachable by UserGoer, UserMaker)
PokerService (reachable by UserGoer, UserDoer)
MokerService (reachable by UserGoer, UserDoer, UserMaker)
If UserDoer requests for JokerService, API returns bad request. JokerService is only reachable for UserGoer and UserMaker. So, I want to restrict and throw an exception.
User Token Structure
public interface IToken
{
string AccessToken { get; set; }
string RefreshToken { get; set; }
}
public class AuthenticationToken : IToken
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
public class UserGoerAuthenticationToken : AuthenticationToken
{
}
public class UserDoerAuthenticationToken : AuthenticationToken
{
}
public class UserMakerAuthenticationToken : AuthenticationToken
{
}
Enum
public enum TokenType
{
Undefined = 0,
UserGoer = 1,
UserDoer = 2,
UserMaker = 3
}
Customized Authentication Attribute
public class AuthenticationFilter : Attribute
{
public TokenType[] TokenTypes { get; private set; }
public AuthenticationFilter(params TokenType[] TokenTypes)
{
this.TokenTypes = TokenTypes;
}
}
Example Service
[AuthenticationFilter(TokenType.UserGoer, TokenType.UserMaker)]
internal class JokerService : BaseService<JokerEntity>
{
public JokerService(IToken AuthenticationToken) : base(AuthenticationToken)
{
var tokenTypes =
(typeof(JokerService).GetCustomAttributes(true)[0] as AuthenticationFilter)
.TokenTypes;
bool throwExceptionFlag = true;
foreach (var item in tokenTypes)
{
// Check AuthenticationToken is UserGoer or UserMaker by StartsWith function
if (AuthenticationToken.GetType().Name.StartsWith(item.ToString()))
{
throwExceptionFlag = false;
break;
}
}
if (throwExceptionFlag)
throw new Exception("Invalid Authentication Token");
}
public JokerEntity Create(RequestModel<JokerEntity> model) => base.Create(model);
public JokerEntity Update(RequestModel<JokerEntity> model) => base.Update(model);
public JokerEntity Get(RequestModel<JokerEntity> model) => base.Get(model);
public List<JokerEntity> List(RequestModel<JokerEntity> model) => base.List(model);
}
In summary, JokerService can be executable by UserGoer and UserMaker. UserDoer has no permission for this service.
As you see the the usage of AuthenticationFilter attribute, I'm getting custom attributes in the constructor, because i want to know what IToken is. If there is an irrelevant "User Authentication Token" type that is passed as parameter (IToken), program should be throw an exception.
This is my solution, do you think is there any best practice for my problem?
Thank you for your help.
Interesting question. My initial thought at constructive critique would be that the tokens accepted by a particular class via the attribute is something decided at compile time and is unable to change. But, the checking for permissions is happening on the construction of each object.
You can prevent this with a static constructor that sets the tokenTypes variable. Static constructors always run before instance constructors. This is also a good place to ensure that tokenTypes is never null (in the absence of your custom attribute).
Likewise, the looping through tokenTypes can probably be a function that takes in an IToken and the tokenTypes, and more importantly, could probably live in the BaseService.cs. Writing that logic once will make it easier to maintain when some future requirement necessitates its change. :)
See also: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
Hope this helps.
Related
I write in C# and TypeScript and have noticed that sometimes cannot do in C# some things. I want to check users permissions, and in C# its going bad - with reflection and repeating Permission keys in Configuration properties, whereas in TypeScript its have done easily. Is there a way to get rid off C# version's disadvantages?
C#
public enum Permission { download, upload }
public class User
{
public string[] Groups { get; set; }
}
public class Configuration
{
public PermissionGroupsSection PermissionGroups { get; set; }
public class PermissionGroupsSection
{
public string Download { get; set; }
public string Upload { get; set; }
}
}
public class Service
{
private readonly Configuration config;
public Service(Configuration config)
{
this.config = config;
}
public void Check(User user, Permission permission)
{
string group = typeof(Configuration.PermissionGroupsSection)
.GetProperty(permission.ToString())
.GetValue(config.PermissionGroups) as string;
if (!user.Groups.Contains(group))
throw new Exception("Access denied");
}
public void DownloadFile(User user, string fileName)
{
Check(user, Permission.download);
Console.WriteLine(fileName + " downloaded!");
}
}
TypeScript
type Permission = "download" | "upload";
interface IUser {
groups: string[];
}
interface IConfiguration {
permissionGroups: { [key in Permission]: string };
}
let config: IConfiguration = JSON.parse(`
"permissionGroups": {
"download": "Everyone",
"upload": "RegisteredUsers"
}
`);
function check(user: IUser, permission: Permission) {
if (!user.groups.includes(config.permissionGroups[permission]))
throw "Access denied";
}
function downloadFile(user: IUser, fileName: string) {
check(user, "download");
alert(fileName + " downloaded!");
}
I wouldn't say that this is a disadvantage of C#, it's just that you have to approach the languages differently. Typescript is still very much a dynamic language, and C# is very much a static language (although lines are starting to get a little blurry...).
When I have a problem like this, I always ask myself what the best data structures are for the needs of the code.
In your case, you need to be able to define permission groups against a set of known actions. In C#, the closest thing to a JSON object you can query by key is a dictionary. So I would declare my permissions something like:
IDictionary<Permission, string> PermissionGroups = new Dictionary<Permission, string>
{
{ Permission.download, "Everyone" },
{ Permission.upload, "RegisteredUsers" }
};
I would say this is now synonymous with the typescript version, as it means your Check method can look like:
public void Check(User user, Permission permission)
{
if (!user.Groups.Contains(PermissionGroups[permission]))
throw new Exception("Access denied");
}
Your code implies that this comes from your config file, in which case this is just creating a new problem elsewhere.
I am purposefully avoiding the discussion about the possible ways to do role-based security.
Say you have a multi-tenant app. A Tenant has various properties:
public class Tenant{
public string TenantName {get; set;}
public string TenantUrl {get; set;}
}
This way when my service layer sends emails, for example, I can do the following:
SendEmail(Tenant.FromEmailAddress, recipientEmailAddress)
This works well for properties. In many places throughout my business logic, I'm encountering cases where tenant-specific behaviors must be accounted for. One example is retrieving photos for the homepage:
public List<string> GetPhotoUrls(){
if(currentTenant == TenantA){
// logic to go off to retrieve from one third party
} else if (currentTenant == TenantB){
// totally different logic
} else... // one for each tenant
// do some stuff
// return stuff
}
GetPhotoUrls is a simple example - but there are cases like this in many places in my business logic. I'm looking for a simple pattern where I can define and implement tenant-specific logic. The overall goal is to get all tenant-specific logic in one place so tenant creation and definition is easy.
I would like the developer experience to read along the lines of:
public List<string> GetPhotoUrls(){
currentTenant.GetPhotoUrls(); // define this logic on the tenant object somehow
// do some stuff
// return stuff
}
What patterns/constructs are available to achieve this?
Use the strategy pattern in your case. The pattern is best applied when you see switch statements or multiple if statements to simplify the client so that it delegates custom implementation to dependent interfaces. You may also use in combination of factory pattern. To illustrate this:
public interface ITenant{
List<string> GetPhotoUrls();
}
public class TenantA:ITenant{
public string TenantName {get; set;}
public string TenantUrl {get; set;}
public List<string> GetPhotoUrls(){
//A implementation
}
}
public class TenantB:ITenant{
public string TenantName {get; set;}
public string TenantUrl {get; set;}
public List<string> GetPhotoUrls(){
//B implementation
}
}
public class SomeTenantApp{
public SomeTenantApp(ITenant tenant){
_tenant = tenant;
}
public void DoSomething(){
var urls = _tenant.GetPhotoUrls();
//do something
}
}
public static class TenantFactory{
public static ITenant Create(string id)
{
//logic to get concrete tenant
return concreteTenant;
}
}
class Program
{
static void Main(string[] args)
{
var tenant = TenantFactory.Create("A");
var app = var SomeTenantApp(tenant);
app.DoSomething();
}
}
The client (SomeTenantApp) won't have to change. You delegated the implementation to the concrete class which owns the logic.
If you want to build SaaS, I'd strongly recommend using ASP.NET Core and dependency injection to overcome multi-tenancy issue.
You can defined your tenant class :
public class AppTenant
{
public string Name { get; set; }
public string[] Hostnames { get; set; }
}
Next you can resolve a tenant from the current request
public class AppTenantResolver : ITenantResolver<AppTenant>
{
IEnumerable<AppTenant> tenants = new List<AppTenant>(new[]
{
new AppTenant {
Name = "Tenant 1",
Hostnames = new[] { "localhost:6000", "localhost:6001" }
},
new AppTenant {
Name = "Tenant 2",
Hostnames = new[] { "localhost:6002" }
}
});
public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context)
{
TenantContext<AppTenant> tenantContext = null;
// it's just a sample...
var tenant = tenants.FirstOrDefault(t =>
t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower())));
if (tenant != null)
{
tenantContext = new TenantContext<AppTenant>(tenant);
}
return tenantContext;
}
}
Wiring it up :
public void ConfigureServices(IServiceCollection services)
{
services.AddMultitenancy<AppTenant, AppTenantResolver>();
}
Getting the current tenant (whenever you need it) :
public class HomeController : Controller
{
private AppTenant tenant;
public HomeController(AppTenant tenant)
{
this.tenant = tenant;
}
.
.
.
}
For more info take a look at SaasKit
Building multi-tenant applications with ASP.NET Core (ASP.NET 5)
Should I use static in the following 2 cases:
Case 1)
public class RequestHeader
{
private string Username { get; set; }
private string Password { get; set; }
private string AccessKey { get; set; }
public string url { get; set; }
public string pageid { get; set; }
public string organizationid { get; set; }
private RequestHeader()
{
}
public static RequestHeader GetRequestHeader(string url, string pageid, string organizationid)
{
return new RequestHeader()
{
Username = "Some logic to fetch username",
Password = "Some logic to fetch password",
AccessKey = "Some access key",
url = url,
pageid = pageid,
organizationid = organizationid,
};
}
}
Case 2)
public class HttpClientHelper
{
public static HttpClient GetHttpClient(RequestHeader header)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
foreach (var property in header.GetType().GetProperties())
{
client.DefaultRequestHeaders.Add(property.Name, property.GetValue(header).ToString());
}
return client;
}
}
I know that static is not used where state is maintained. I believe I am not maintaining any state here. I will be using this in a class library and I will be using these for calling a rest service.
The only thing which makes me want to use static here is not to initialize these class.(I know this is a very baaad reason).
Please let me know your thoughts. Is there something which I am not seeing in this.
Note: 1) I am aware of the small casing for some of the properties. It is in sync with the rest service on which I have absolutely no control.
2) If I have multiple RequestHeader in future, I might create an IRequestHeader which has a method GetRequestHeader. So the different RequestHeaders will implement this. In this case I know I cant keep a static method in interface.
Please Keep these 2 conditions away and let me know your thoughts.
What you have here seems to be a version of the Static Factory Pattern. This is a well-known pattern and is perfectly fine to use.
You might also be interested in the non-static version of the Factory Pattern.
I assume HttpClient is not "your class", in which case you of course can't add a method inside the class itself.
The only thing which makes me want to use static here is not to initialize these class.(I know this is a very baaad reason).
Technically you're instantiating and initializing these classes no matter how you do it (factory method or no factory method), the only question is if you are going to use a factory method to do the instantiation and initialization for you.
If you have to use same values for each call you should use static fields, because static fields are used when only one copy of the variable is required. The same static field will share the copy across all the instances.
How can I access a ServiceStack.net session in my validation code?
public class UserSettingsValidator : AbstractValidator<UserSettingsRequest>
{
public UserSettingsValidator()
{
RuleFor(x => x.UserId)
.SetValidator(new PositiveIntegerValidator())
.SetValidator(new UserAccessValidator(session.UserId)); //<-- I need to pass the UserID from the session here
}
}
In the Service Implementation I just do:
var session = base.SessionAs<UserSession>();
but this does not work for my abstract validator.
Thanks!
Edit: this is version 3.9.71.0
I assume you are just using the ValidationFeature plugin, as most do. If that's the case, then I don't think it is possible. Ultimately the ValidationFeature is a plugin which uses a RequestFilter.
I wanted to do something similar before too, then realised it wasn't possible.
The RequestFilter is run before the ServiceRunner. See the order of operations guide here.
What this means to you is your populated request DTO reaches your service, and the validation feature's request filter will try validate your request, before it has even created the ServiceRunner.
The ServiceRunner is where an instance of your service class becomes active. It is your service class instance that will be injected with your UserSession object.
So effectively you can't do any validation that relies on the session at this point.
Overcomplicated ?:
It is possible to do validation in your service method, and you could create a custom object that would allow you pass the session along with the object you want to validate. (See next section). But I would ask yourself, are you overcomplicating your validation?
For a simple check of the request UserId matching the session's UserId, presumably you are doing this so the user can only make changes to their own records; Why not check in the service's action method and throw an Exception? I am guessing people shouldn't be changing this Id, so it's not so much a validation issue, but more a security exception. But like I say, maybe your scenario is different.
public class SomeService : Service
{
public object Post(UserSettingsRequest request) // Match to your own request
{
if(request.UserId != Session.UserId)
throw new Exception("Invalid UserId");
}
}
Validation in the Service Action:
You should read up on using Fluent Validators. You can call the custom validator yourself in your service method.
// This class allows you to add pass in your session and your object
public class WithSession<T>
{
public UserSession Session { get; set; }
public T Object { get; set; }
}
public interface IUserAccessValidator
{
bool ValidUser(UserSession session);
}
public class UserAccessValidator : IUserAccessValidator
{
public bool ValidUser(UserSession session)
{
// Your validation logic here
// session.UserId
return true;
}
}
public class UserSettingsValidator : AbstractValidator<WithSession<UserSettingsRequest>>
{
public IUserAccessValidator UserAccessValidator { get; set; }
public UserSettingsValidator()
{
// Notice check now uses .Object to access the object within
RuleFor(x => x.Object.UserId)
.SetValidator(new PositiveIntegerValidator());
// Custom User Access Validator check, passing the session
RuleFor(x => x.Session).Must(x => UserAccessValidator.ValidUser(x));
}
}
Then to actually use the validator in your service:
public class SomeService : Service
{
// Validator with be injected, you need to registered it in the IoC container.
public IValidator<WithSession<UserSettingsRequest>> { get; set; }
public object Post(UserSettingsRequest request) // Match to your own request
{
// Combine the request with the current session instance
var requestWithSession = new WithSession<UserSettingsRequest> {
Session = this.Session,
Object = request
};
// Validate the request
ValidationResult result = this.Validator.Validate(requestWithSession);
if(!result.IsValid)
{
throw result.ToException();
}
// Request is valid
// ... more logic here
return result;
}
}
I hope this helps. Note: code is untested
It appears that after reading from a bunch of people experiencing similar problems, then many hours of playing with several solutions based on the SS4 Cookbook etc, this is a problem that is already solved:
https://forums.servicestack.net/t/blaz-miheljak-355-feb-3-2015/176/2
Implement the IRequiresRequest interface on your validator, and voila.
I have a class called "Account".
Here are my codes:
// Account.cs
public partial class Account
{
private Account.Credential _credential = new Account.Credential();
public void Login(string UserID, string UserPW)
{
try
{
// do something
_credential.CookieCollection = browser.CookieCollection;
_credential.CookieContainer = browser.CookieContainer;
_credential.UserID = "test";
_credential.UserPW = "test";
}
catch (Exception err)
{
throw err;
}
}
}
// Credential.cs
public partial class Account
{
public class Credential
{
// Model
public CookieCollection CookieCollection { get; set; }
public CookieContainer CookieContainer { get; set; }
public string UserID { get; set; }
public string UserPW { get; set; }
}
}
// Form1.cs
public void ABC()
{
Account[] _account = new Account[2];
_account[0].Login("myID", "myPW");
Account.Credential _cred = _account[0].Credential; ---> I get an error.
}
But when I write a mothod call the Account class in array and call the sub class which is Credential, it gives me an error.
'Credential': cannot reference a type through an expression; try 'Account.Credential' instead.
Because, Login method is in Account Class I should have make an array of Account class. Not Credential class.
Does anyone know how to fix this?
As the error is trying to tell you, the Credential type is a part of the Account type; not any single Account instance.
It makes no sense to refer to the type myAccounts[42].Credential or mikesAccount.Credential.
If you want to get that user's credential, you'll need to refer to a field or property of the Account class.
You could write myAccounts[42]._credential, except that _credential is private, so that will give a different error.
You should make a public, probably-read-only, property.
This is happening because Account.Credential is an internal type to Account and isn't used like how you're using it. What code is attempting to do would be similar to trying
var str = String;
You need to initialize an Account.Credential. A simple was is to use new inside Account's ctor or the Login() method.
I noticed you're going to run into further problems with your code - if you want to access the Account.Credential reference declared inside Account, you'll need to expose it through a method or change the access modifier.