I'm trying to create a service which manages different account providers and accounts within our application (WPF desktop app).
My idea was to have account providers, which would be external web services such as Jira, Gitlab, etc. Each provider has a list with accounts.
Depending on the provider it can have different means to authorize our software to use a users account. Our first implementation is using OAuth tokens for authorization and a Rest API to interact with a web service.
Obviously, we need some restrictions e.g. an OAuthProvider will only accept OAuthAccounts in its Accounts list.
The ideal case would be if we could store all providers no matter their accounts type in a single list of the form List<IAccountProvider<IAccount>> someList. However I couldn't get it to compile. The error is:
Error CS1503 Argument 1: cannot convert from 'TestProject.Program.JiraProvider' to 'TestProject.Program.IAccountProvider<TestProject.Program.IAccount>'
I tried to make IAccountProvider covariant but this in turn restricts me very much in using the interface...
Is there some way to get it to compile using this class hierarchy? If you have a better idea how to setup the hierarchy then I'd be glad to hear your suggestions :).
static int Main(string[] args)
{
var jiraProv = new JiraProvider();
//var someList = new List<IAccountProvider<OAuthAccount>>(); // this would work - but it's not what I want...
var someList = new List<IAccountProvider<IAccount>>(); // this doesn't seem to work
someList.Add(jiraProv); // error CS1503 cannot convert from X to Y
return 0;
}
//-----------------
// Interfaces - details left out for brevity...
// Providers
public interface IAccountProvider<T>
where T : IAccount
{
List<T> Accounts { get; }
T CurrentAccount { get; }
void Login(T account);
}
public interface IOAuthProvider<T> : IAccountProvider<T>
where T : IOAuthAccount
{ }
// Accounts
public interface IAccount { }
public interface IOAuthAccount : IAccount { }
//-----------------
// Implementations - details left out for brevity...
public class OAuthAccount : IOAuthAccount { }
public class JiraProvider : IOAuthProvider<OAuthAccount>
{
public List<OAuthAccount> Accounts { get; set; }
public OAuthAccount CurrentAccount { get; set; }
public void Login(OAuthAccount account) { }
}
Related
Here is the problem
A multi-layered approach. The end point/action used BLL Provider which must trim data not only using certain business criteria but also security criteria. For example, a super user can view all items and plain user can only view items assigned to his group.
The knee-jerk reaction to this - public IEnumerable<Item> GetItems(int? userId, string color, string location), where if user Id is not provided - get all items. But what if I want to put additional layer of protection - derive query based on a special MyIdentityProvider.
public class BllItemDataProvider : IBllItemDataProvider
{
private IMyIdentityProvider _myIdentity;
public BllItemDataProvider(IMyIdentityProvider myIdentity)
{
_myIdentity = myIdentity;
}
public BllItemDataProvider(IMyIdentityProvider myIdentity)
{
_myIdentity = myIdentity;
}
}
MyIdentityProvider would have userId, isSuperUser, other flags, etc.
The question is, how to wire it up so that in Web API IHttpContextAccessor or the HttpContext will hydrate MyIdentityProvider from the Claims I find in the [controller].HttpContext.User.Claims. So my controller would look like
public class ItemController : ControllerBase
{
private IBllItemDataProvider _prov
public ItemController (IBllItemDataProvider prov)
{
_prov = prov;
}
[HttpGet("[action]/{color}/{location?}")]
public IActionResult SomeItems (string color, string location)
{
_prov = prov.GetItems(color, location);
}
}
Or, I should just create a base controller which can obtain that and inherit from it?
public abstract class ClaimControllerBase: ControllerBase - in constructor parse claims and setup thread principal, which can then be accessed down in the pipeline?
Thanks
Additionally I can add that these flags in the MyIdentityProvider will be taken from HttpContext.User.Claims which in turn come from JWT token and filled by Identity Framework. So, literally, I can take those claims and slap it in the base controller into a thread. But I don't think this a pretty solution.
Alright, looks like this one goes into DIY, or solve it yourself
Added this code
// CREATED Helper
public class MyIdentityProviderHelper
{
public static IMyIdentityProvider FetchProvider(IHttpContextAccessor accessor)
{
var prov = new MyIdentityProvider();
// -- HERE parse claims and fill properties
return prov;
}
}
// ADDED to Startup
services.AddHttpContextAccessor();
services.AddTransient<IMyIdentityProvider>((s) => // factory method is the real secret here
{
return MyIdentityProviderHelper.FetchProvider(
s.GetService<IHttpContextAccessor>());
});
services.AddTransient<IItemProvider, RealItemProvider>();
// DECLARED RealItemProvider
public class RealItemProvider : IItemProvider
{
private IMyIdentityProvider _identity
public RealItemProvider (IMyIdentityProvider identity)
{
_identity = identity;
}
public IEnumerable<Item> GetItems(string color, string location)
{
IEnumerable<Item> items = null;
if (_identity.Roles.Contains(Roles.SysAdmin))
items = GetAllItems(color, location); // private
else
items = GetUserItems(color, location); // private
return items;
}
}
As you see, the main soul saver here is ability to provide a factory for the object and use factory of another object to pass the retrieved object
I have a clean architecture project that provide micro services, one of which is to access Agresso ERP web services.
https://***************/service.svc
it provide many services
https://**/service.svc?FooService/Foo
https://**/service.svc?BooService/Boo
each of which has it's own service reference(connected service), and each of which has many methods.
each call to any of the end point you need to pass credentials with it.
var fooSoapClient = new FooSoapClient();
var credentials = new WSCredentials
{
Username = "fakeuser",
Password = "fakepassword",
Client = "fakeclient",
};
var result = fooSoapClient.GetFoosAsync(Foo filter,true,
credentials );
(P.S) credential class exist in all entities
namespace Foo1NS
{
public partial class WSCredentials : object
{
public string Username {get;set;}
public string Client {get;set;}
public string Password {get;set;}
}
}
namespace Foo2NS
{
public partial class WSCredentials : object
{
public string Username {get;set;}
public string Client {get;set;}
public string Password {get;set;}
}
}
i can access all end points with no problem.
I have the following Questions:
Is there a generic solution i can follow for not to Fall in DRY?
is there a design pattern that best target this issue?
Here is what I've done in the past, it fits in well into Dependency Injection/containers if you use that as well. The key thing here is to define an single interface that all services will implement. Your code that uses this should only be using the interface.
Each class should implement an interface you define, e.g. IWebServiceOperations
public interface IWebServiceOperations
{
WebServiceOperationResult GetFooAsync(WebServiceOperationRequest request);
}
I'll leave you to figure out the classes WebServiceOperationResult/Request, they just hold your request/response variables, including credentials.
Then each webservice you need to implement is done in a separate class. You also dictate in the constructor what type of implementation this is (FooSoap1 vs FooSoap2) e.g.
public class FooSoapClient : BaseClient, IWebServiceOperations
{
public FooSoapClient() : base(Clients.FooSoap1)
public GetFooAsync(...)
{
...
}
}
public class BaseClient
{
private readonly eFooServiceType _serviceType;
public eFooServiceType ServiceType {
get{
return _serviceType;
}
}
protected BaseClient(eFooServiceType service)
{
_serviceType = service;
}
}
Now you should have a bunch of class references. Either your DI container can resolve these for you, based on the service type you want, or you could add them to a Dictionary, so if you wanted to operate against FooSoap1, you'd do...
var fooSoapClient1 = myServices[Clients.FooSoap1];
await fooSoapClient1.GetFooAsync(...)
I have a WCF Service with the following operation contract:
[OperationContract]
Response SearchEntities(Query query);
This operation takes a request that contains a specified Entity like so:
[DataContract]
public class Query
{
[DataMember]
public string SearchTerm { get; set; }
[DataMember]
public string Entity { get; set; }
[DataMember]
public bool ExactMatch { get; set; }
}
Based on the value contained within the Entity property, one the following properties is populated within this response:
[DataContract]
public class Response
{
[DataMember]
public List<Asset> Assets { get; set; }
[DataMember]
public List<Stage> Stages { get; set; }
[DataMember]
public List<Sector> Sectors { get; set; }
}
Terrible design, I know! However. I am using Autofac.Wcf as my service factory to inject dependencies. Normally I would use a common Interface and Generics to determine a service to use based on the Entity value like so:
public interface IEntitySearch<T>
{
Response Search(Query query);
}
The above interface would have several implementations for each of the Lists within the response. Using a design pattern such as a service location I could determine which service to use (all of which inherit from IEntitySearch<T>, something like:
public IEntitySearch ResolveSearcher(Query query)
{
switch(query.Entity)
{
case "Assets":
return _container.Resolve<AssetSearch>();
case "Stages":
return _container.Resolve<StageSearch>();
default:
throw new NotSupportedException();
}
}
While this works, a more elegant solution (I believe) would be to customize the Autofac container per request for this particular operation, depending on the data contained within the request.
IE: Before the WCF pipe line sends the request to the service implementation, is it possible to examine the request data and customize how the container resolves dependencies. That way I can avoid exposing dependency resolution within my service layer.
Is this possible?
If another DI library other than Autofac has a solution for this, I will happily change our DI framework.
Thanks.
I haven't personally tried this but I think a direction you can go down is to combine:
Using OperationContext.Current to get the current request message data.
Specifying a custom IServiceImplementationDataProvider for Autofac that tells Autofac which WCF interface to host for that request.
Using a lambda registration for your service implementation to switch the backing service based on OperationContext.Current.
You can see two examples of the IServiceImplementationDataProvider by looking at the DefaultServiceImplementationProvider - the one that works in Autofac WCF hosting by default; andMultitenantServiceImplementationDataProvider, which is more about generating a proxy to enable multitenant WCF hosting.
While neither of these use OperationContext.Current to determine the actual backing service, you can build on the ideas:
Look at the Autofac.Multitenant.Wcf implementation. You may be able to use it as-is. The point of the instance data provider there is that WCF grabs on to the concrete type of the service being hosted and if you try to swap types out from under it, you get errors. The multitenant support fools WCF by creating a proxy type and your implementation type can be swapped out under the proxy. Note the MultitenantServiceImplementationDataProvider doesn't actually tie anything to a tenant or tenant ID; it's only about that proxy.
In your .svc file specify a service interface rather than any individual concrete implementation since you'll be swapping out the implementation.
Use a lambda registration to figure out your implementation.
Make sure your service is InstanceContextMode.PerCall to ensure things get swapped out on a per request basis.
The registration might look something like this:
builder.Register(ctx => {
var context = OperationContext.Current;
var type = DetermineTypeFromContext(context);
return ctx.Resolve(type);
}).As<IMyServiceInterface>();
The Autofac WCF and Autofac Multitenant section on WCF may also help.
In my opinion you're trying to move your problem just to another place. Why would making decision based on request at low-level WCF is better than switch in SearchEntities method? It's much worse ;-)
I would consider to use IEntitySearch factory/provider e.q.IEntitySearchProvider (it's not so much better but always).
public interface IEntitySearch
{
bool IsMatchQuery(Query query);
Response Search(Query query);
}
// without service locator
public class EntitySearchProvider : IEntitySearchProvider
{
private readonly IEnumerable<IEntitySearch> _searchers;
public EntitySearchProvider(IEnumerable<IEntitySearch> searchers)
{
_searchers = searchers;
}
public IEntitySearch GetSearcher(Query query)
{
// last registered
return _searchers.LastOrDefault(i=>i.IsMatchQuery(query))
?? throw new NotSupportedException();
}
}
or
public interface IEntitySearchProvider
{
IEntitySearch GetSearcher(Query query);
}
public class EntitySearchProvider : IEntitySearchProvider
{
private readonly IComponentContext _container;
public EntitySearchProvider(IComponentContext container)
{
_container = container;
}
public IEntitySearch GetSearcher(Query query)
{
switch(query.Entity)
{
case "Assets":
return _container.Resolve<AssetSearch>();
case "Stages":
return _container.Resolve<StageSearch>();
default:
throw new NotSupportedException();
}
}
}
with
public class WcfService
{
private readonly IEntitySearchProvider _provider;
public WcfService(IEntitySearchProvider provider)
{
_provider = provider;
}
public Response SearchEntities(Query query)
{
var searcher = _provider.GetSearcher(query);
return searcher.Search(query);
}
}
I have this api client ICommunicationClient(url, tenant) registered in my IoC container. Now I'm facing the scenario where I can have 1 to n api clients. I need to register all of them and I'm not sure how to handle that. I've seen there's this RegisterCollection in SI though.
I'm considering use a ICommunicationClientProvider as a wrapper around the actual clients. It contains a list with all the registered clients and methods to retrieve them. I feel this is not the best approach and of course, it "forces" me to touch other pieces of the app.
public class CommunicationClientProvider : ICommunicationClientProvider
{
public CommunicationClientCollection CommunicationClientsCollection { get; set; }
public string Tenant { get; set; }
public ICommunicationClient GetClients()
{
return CommunicationClientsCollection[Tenant];
}
public void SetClients(CommunicationClientCollection clients)
{
CommunicationClientsCollection = clients;
}
}
public interface ICommunicationClientProvider
{
ICommunicationClient GetClients();
void SetClients(CommunicationClientCollection clients);
}
This to host the collection
public class CommunicationClientCollection : Dictionary<string, ICommunicationClient>
{
}
Here I register the collection against SI
var clients = new CommunicationClientProvider();
foreach (var supportedTenant in supportedTenants)
{
clients.CommunicationClientsCollection
.Add(supportedTenant, new CommunicationClient(
new Uri(configuration.AppSettings["communication_api." + supportedTenant]),
new TenantClientConfiguration(supportedTenant)));
}
container.RegisterSingleton<ICommunicationClientProvider>(clients);
Do you know a better way of doing this? This is a normal scenario for example when you have multiple databases.
UPDATE: - ITenantContext part -
This is basically how my tenant context interface looks like:
public interface ITenantContext
{
string Tenant { get; set; }
}
and this is where I'm making my call to communication api:
public class MoveRequestedHandler : IHandlerAsync<MoveRequested>
{
private readonly IJctConfigurationService _communicationClient;
private readonly ITenantContext _tenantContext;
public MoveRequestedHandler(IJctConfigurationService communicationClient, ITenantContext tenantContext)
{
_communicationClient = communicationClient;
_tenantContext = tenantContext;
}
public async Task<bool> Handle(MoveRequested message)
{
_tenantContext.Tenant = message.Tenant;
_communicationClient.ChangeApn(message.Imei, true);
return await Task.FromResult(true);
}
}
here I register the ITenantContext
container.RegisterSingleton<ITenantContext, TenantContext>();
The tenant is defined within the MoveRequested object (message.Tenant).
How can I make CommunicationClient aware of that tenant?
If adding an ICommunicationClientProvider abstraction causes you to make sweeping changes throughout your application, there is clearly something wrong. You should typically be able to add features and make changes without having to do sweeping changes. And as a matter of fact, I think your current design already allows this.
Your ICommunicationClientProvider) acts like a factory, and factories are hardly ever the right solution. Instead, your are much better of using the Composite design pattern. For instance:
sealed class TenantCommunicationClientComposite : ICommunicationClient
{
private readonly ITenantContext tenantContext;
private readonly Dictionary<string, ICommunicationClient> clients;
public TenantCommunicationClientComposite(ITenantContext tenantContext,
Dictionary<string, ICommunicationClient> clients) {
this.tenantContext = tenantContext;
this.clients = clients;
}
object ICommunicationClient.ClientMethod(object parameter) =>
this.clients[this.tenantContext.CurrentTenantName].ClientMethod(parameter);
}
You can register this class as follows:
var dictionary = new Dictionary<string, ICommunicationClient>();
foreach (var supportedTenant in supportedTenants) {
dictionary.Add(supportedTenant, new CommunicationClient(
new Uri(configuration.AppSettings["communication_api." + supportedTenant]),
new TenantClientConfiguration(supportedTenant)));
}
container.RegisterSingleton<ICommunicationClient>(
new TenantCommunicationClientComposite(
new AspNetTenantContext(),
dictionary));
Here the ITenantContext is an abstraction that allows you to get the current tenant on who's behalf the current request is running. The AspNetTenantContext is an implementation that allows you to retrieve the current tenant in an ASP.NET application. You probably already have some code to detect the current tenant; you might need to move that code to such AspNetTenantContext class.
I'm adjusting my web application layers in a way to make the code more testable.
Currently the UI talks to a service locator passing in an interface, this returns the appropriate object based on that type:
ServiceLocator.Get<ISomeService>().ListStuff(arg1, arg2);
Internally, services are instantiated with an instance of IServiceContext and cached.
private static Lazy<IDictionary<Type, object>> _services = new Lazy<IDictionary<Type, object>>(GetServices);
public interface IServiceContext
{
IConfiguration Configuration { get; }
IUser CurrentUser { get; internal set; }
ILogProvider Log { get; }
ICacheProvider Cache { get; }
IProfilerProvider Profiler { get; }
}
public LogService(IServiceContext serviceContext)
: base(serviceContext) { }
I'm happy with the concept and it appears to be rugged enough, my only issue is I want to make the current logged in user available in the ServiceContext but unsure the best way to achieve it.
My thoughts travel along these potential options:
Keep a simple method in the ServiceLocator that handles getting the users session and injects it into the services as requests for them come in.
Move getting the current user out of the IServiceContext and into the ServiceBase base class of each service.
Stop doing this and make each service that needs a user dependent on it.
I appreciate any suggestions, I understand this question my not be in the true spirit of the site.
I have managed 4 days of trial and error to get to this point, just need the last piece of this puzzle.
There's probably many solutions, and I'm not fully sure I understand your question but I'll try to help anyway.
Whenever I need the current user I make a call to static utility class right in the context of the code that uses it. This way I eliminate the possibility of stale information.
You could make a class that implements IUser like
class User : IUser {
private System.Security.Principal.WindowsIdentity identity;
public User() {
this.identity = identity = System.Security.Principal.WindowsIdentity.GetCurrent();
}
public string UserName { get { return this.identity.Name; } }
}
And then maybe:
public class ServiceContext : IServiceContext
{
IUser CurrentUser { get { return new User(); } }
}