How to mock external dependencies using JustMock Lite - c#

I am using free version of JustMock to mock some external dependencies like GetUserSettingsResponse which is a sealed class. The problem is that the free version does not allow us to mock sealed classes. I cannot use the full version because of some reasons. Below is my code sample:
public GetUserSettingsResponse GetUserSettingsResponse(string emailAddress)
{
var response = service.GetUserSettings(
emailAddress,
UserSettingName.ExternalEwsUrl,
UserSettingName.InternalEwsUrl,
UserSettingName.EwsSupportedSchemas);
return response;
}
This is the method I am trying to mock as below:
[TestMethod]
public void GetExchangeService()
{
//Arrange
string url = "";
GetUserSettingsResponse response;
ObjectOfMyClass.Arrange(x => x.GetUserSettingsResponse(Arg.IsAny<string>())).Returns(new GetUserSettingsResponse()); //this is where it throws exception saying that only non-sealed classes can be mocked with lite version of just mock
}
Edit: My application is a Web service which is basically using EWS managed APIs to get the email account details from the exchange server. So, in order to start the communication I am first doing the AutoDiscovery to get the url out of email address of the user. So the subject under test is below method which internally calls GetUserSettingsResponse method:
public class MyClass
{
public ExchangeService GetExchangeService(
string userName,
string password,
int version,
string emailAddress,
string userId)
{
AutoDiscoverService service = new AutodiscoverService();
service.UseDefaultCredentials = false;
service.Credentials = new NetworkCredential(userName, password);
service.EnableScpLookup = true;
service.ReturnClientRequestId = true;
service.PreAuthenticate = false;
service.RedirectionUrlValidationCallback = RedirectionUrlValidationCallback;
var url = string.Empty;
var response = GetUserSettingsResponse(emailAddress);
if (response.TryGetSettingValue(UserSettingName.ExternalEwsUrl, out settingValue)
|| response.TryGetSettingValue(UserSettingName.InternalEwsUrl, out settingValue))
{
ewsurl = settingValue;
}
var exchangeService = new ExchangeService((ExchangeVersion)version)
{
Credentials = new WebCredentials(userName, password),
KeepAlive = true,
Url = new Uri(ewsurl)
};
return exchangeService;
}
}

Related

How can I make multiple SOAP web service calls generic to reduce redundancy?

I have these methods below that call some SOAP web services, all from the same provider, so they all have the same methods/calls/etc. I'm looking for a more OOP/abstract way to call these without writing so many methods? Ideally I'd like one method for each -> GetClaim(), AddClaim(), SearchClaim(), RemoveClaim(), etc.
Question - Should I pass in the parameters specific to the service to make the method more generic, there by eliminating 15 other methods all like this or is there a better more oop/abstract approach? Can somebody please provide me with an example?
// ex. how can I make these two methods 1?
public async void ClaimSearchForWRG(string url, string userName, string password) {
var client = new WebServiceWRGClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<WebServiceWRG>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new User();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var response = await client.ClaimSearchAsync(user, "", "", 12345, statuscode.NotSet, "");
}
// another call (same provider) with the same call -> ClaimSearchAsync()
public async void ClaimSearchForAWI(string url, string userName, string password) {
var client = new WebServiceAWIClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<WebServiceAWI>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new ArmUser();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var response = await client.ClaimSearchAsync(user, "", "", 12345, ArmStatuscode.NotSet, "");
}
// then we have 15 other web service calls from the same provider for ClaimSearchAsync()
// then we have 15 more calls for ClaimGetAsync()
// then we have 15 more calls for AddClaimAsync()
// then we have 15 more calls for RemoveClaimAsync()
// etc, etc, etc
UPDATED After trying this code below to make things a little more generic (to eliminate redundancy) I'm getting some errors in the code. Specifically related to the compiler not finding the properties associated with the generic entities I'm passing into the method. ex. user.Username is not found -> error message says "'TTwo' does not contain a definition for 'UserName'"
public class Test {
public void TestWebService() {
var ws = new WebService<WebServiceWRG>();
ws.SearchClaim(new WebServiceWRGClient(), new GraceUser(),
"https://trustonline.delawarecpf.com/tows/webservicewrg.svc", "userName", "password");
}
}
public class WebService<T> {
public void SearchClaim<TOne, TTwo>(TOne entity1, TTwo entity2, string url, string userName, string password)
where TOne : class
where TTwo : class
{
var client = entity1;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = entity2;
user.UserName = webService.EncryptValue(userName);
user.Password = webService.EncryptValue(password);
var response = client.ClaimSearch(user, "", "", 12345, GraceStatuscode.NotSet, "");
}
}
UPDATED I was asked to show what "ClaimSearchAsync" does or what it is. I copied this from the web service reference file that was generated from dotnet
System.Threading.Tasks.Task<GRACE_GRACES.WebServiceResult> ClaimSearchAsync(GRACE_GRACES.User user, string ssn, string lastname, int claimnumber, GRACE_GRACES.statuscode statuscode, string assignedto);
as this is a web service, there is no method or code behind that shows what it does.
The provided example methods all violate Single Responsibility Principle (SRP) and Separation of Concerns (SoC) so that is where I started in trying to make them more generic.
The creation of the service and service client should be abstracted out into their own concerns
For example, the web services can be created via a generic factory abstraction
public interface IWebServiceFactory {
TWebService Create<TWebService>(string uri);
}
and simple implementation which encapsulates the creation of the channel factory using the provided URL.
public class ServiceFactory : IWebServiceFactory {
public TWebService Create<TWebService>(string url) {
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport) {
MaxReceivedMessageSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue
};
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<TWebService>(binding, endpoint);
TWebService webService = channelFactory.CreateChannel();
return webService;
}
}
The service clients' creation can also be abstracted out into it own concern.
public interface IClientFactory {
TClient Create<TClient>() where TClient : class, new();
}
to be implemented based on the common definition of your clients.
Now for the creation of a generic client you need to take the common functionality expected from the types involved with the member to be invoked.
This can allow for a convention to be used for the expected types. Dynamic expressions were used to construct the conventions applied.
Resulting in the following helpers for the SearchClaimAsync
static class ExpressionHelpers {
public static Func<string, string, TUserResult> CreateUserDelegate<TUserResult>() {
var type = typeof(TUserResult);
var username = type.GetProperty("username", BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
var password = type.GetProperty("password", BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
//string username =>
var usernameSource = Expression.Parameter(typeof(string), "username");
//string password =>
var passwordSource = Expression.Parameter(typeof(string), "password");
// new TUser();
var user = Expression.New(type);
// new TUser() { UserName = username, Password = password }
var body = Expression.MemberInit(user, bindings: new[] {
Expression.Bind(username, usernameSource),
Expression.Bind(password, passwordSource)
});
// (string username, string password) => new TUser() { UserName = username, Password = password }
var expression = Expression.Lambda<Func<string, string, TUserResult>>(body, usernameSource, passwordSource);
return expression.Compile();
}
public static Func<TService, string, Task<string>> CreateEncryptValueDelegate<TService>() {
// (TService service, string name) => service.EncryptValueAsync(name);
var type = typeof(TService);
// TService service =>
var service = Expression.Parameter(type, "service");
// string name =>
var name = Expression.Parameter(typeof(string), "name");
// service.EncryptValueAsync(name)
var body = Expression.Call(service, type.GetMethod("EncryptValueAsync"), name);
// (TService service, string name) => service.EncryptValueAsync(name);
var expression = Expression.Lambda<Func<TService, string, Task<string>>>(body, service, name);
return expression.Compile();
}
public static Func<TClient, TUser, Task<TResponse>> CreateClaimSearchDelegate<TClient, TUser, TResponse>() {
var type = typeof(TClient);
// TClient client =>
var client = Expression.Parameter(type, "client");
// TUser user =>
var user = Expression.Parameter(typeof(TUser), "user");
var method = type.GetMethod("ClaimSearchAsync");
var enumtype = method.GetParameters()[4].ParameterType; //statuscode
var enumDefault = Activator.CreateInstance(enumtype);
var arguments = new Expression[] {
user,
Expression.Constant(string.Empty), //ssn
Expression.Constant(string.Empty), //lastname
Expression.Constant(12345), //claimnumber
Expression.Constant(enumDefault), //statuscode
Expression.Constant(string.Empty)//assignto
};
// client.ClaimSearchAsync(user, ssn: "", lastname: "", claimnumber: 12345, statuscode: default(enum), assignedto: "");
var body = Expression.Call(client, method, arguments);
// (TClient client, TUser user) => client.ClaimSearchAsync(user,....);
var expression = Expression.Lambda<Func<TClient, TUser, Task<TResponse>>>(body, client, user);
return expression.Compile();
}
}
Take some time to review the comments to get a better understanding of what is being done.
The generic web service can then be defined as follows
public class WebService<TWebServiceClient, TWebService, TUser>
where TWebService : class
where TWebServiceClient : class, new()
where TUser : class, new() {
/// <summary>
/// Create user object model
/// </summary>
private static readonly Func<string, string, TUser> createUser =
ExpressionHelpers.CreateUserDelegate<TUser>();
/// <summary>
/// Encrypt provided value using <see cref="TWebService"/>
/// </summary>
private static readonly Func<TWebService, string, Task<string>> encryptValueAsync =
ExpressionHelpers.CreateEncryptValueDelegate<TWebService>();
private readonly IWebServiceFactory serviceFactory;
private readonly IClientFactory clientFactory;
Lazy<TWebServiceClient> client;
public WebService(IWebServiceFactory serviceFactory, IClientFactory clientFactory) {
this.serviceFactory = serviceFactory ?? throw new ArgumentNullException(nameof(serviceFactory));
this.clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory));
client = new Lazy<TWebServiceClient>(() => clientFactory.Create<TWebServiceClient>());
}
public async Task<TResponse> SearchClaimAsync<TResponse>(WebServiceOptions options) {
TWebService webService = serviceFactory.Create<TWebService>(options.URL);
TUser user = createUser(
await encryptValueAsync(webService, options.UserName),
await encryptValueAsync(webService, options.Password)
);
Func<TWebServiceClient, TUser, Task<TResponse>> claimSearchAsync =
ExpressionHelpers.CreateClaimSearchDelegate<TWebServiceClient, TUser, TResponse>();
TResponse response = await claimSearchAsync.Invoke(client.Value, user);
return response;
}
//...other generic members to be done
}
public class WebServiceOptions {
public string URL { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
The code it self is decoupled enough from implementation concerns to allow for it to be tested in isolation to ensure that it behaves as expected.
As demonstrated in the following unit tested
[TestClass]
public class GenericWebServiceTests {
[TestMethod]
public void Should_Create_New_WebService() {
//Arrange
var serviceFactory = Mock.Of<IWebServiceFactory>();
var clientFactory = Mock.Of<IClientFactory>();
//Act
var actual = new WebService<WebServiceWRGClient, IWebService, User1>(serviceFactory, clientFactory);
//Assert
actual.Should().NotBeNull();
}
[TestMethod]
public async Task Should_ClaimSearchAsync() {
//Arrange
var service = Mock.Of<IWebService>();
Mock.Get(service)
.Setup(_ => _.EncryptValueAsync(It.IsAny<string>()))
.ReturnsAsync((string s) => s);
var serviceFactory = Mock.Of<IWebServiceFactory>();
Mock.Get(serviceFactory)
.Setup(_ => _.Create<IWebService>(It.IsAny<string>()))
.Returns(service);
var clientFactory = Mock.Of<IClientFactory>();
Mock.Get(clientFactory)
.Setup(_ => _.Create<WebServiceWRGClient>())
.Returns(() => new WebServiceWRGClient());
string url = "url";
string username = "username";
string password = "password";
var options = new WebServiceOptions {
URL = url,
UserName = username,
Password = password
};
var webService = new WebService<WebServiceWRGClient, IWebService, User1>(serviceFactory, clientFactory);
//Act
var actual = await webService.SearchClaimAsync<WebServiceResult>(options);
//Assert
//Mock.Get(serviceFactory).Verify(_ => _.Create<IService1>(url));
//Mock.Get(service).Verify(_ => _.EncryptValue(username));
//Mock.Get(service).Verify(_ => _.EncryptValue(password));
//Mock.Get(clientFactory).Verify(_ => _.Create<Client1>());
actual.Should().NotBeNull();
}
#region Support
public class User1 {
public string UserName { get; set; }
public string Password { get; set; }
}
public class User2 {
public string UserName { get; set; }
public string Password { get; set; }
}
public class WebServiceWRGClient {
public Task<WebServiceResult> ClaimSearchAsync(User1 user, string ssn, string lastname, int claimnumber, statuscode statuscode, string assignedto) {
return Task.FromResult(new WebServiceResult());
}
}
public enum statuscode {
NotSet = 0,
}
public class Client2 { }
public interface IWebService {
Task<string> EncryptValueAsync(string value);
}
public interface IService2 {
Task<string> EncryptValueAsync(string value);
}
public class Service1 : IWebService {
public Task<string> EncryptValueAsync(string value) {
return Task.FromResult(value);
}
}
public class WebServiceResult {
}
#endregion
}
This should be enough to get you started in reviewing the other members to be made generic. The above provided code has been tested and works as expected based on what was provided in the original question.
Do note that this does seem like a large task depending on the amount of members to be refactored. You should take some time to make sure the effort is even worth it.
You have classical Divergent Change smell here.
Signs and Symptoms. You find yourself having to change many unrelated methods when you make changes to a class. For example, when adding a new product type you have to change the methods for finding, displaying, and ordering products.
I suggest to make refactoring to Abstract Factory pattern. You will separate web service and object creation logic.
Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.
So you will have something like:
And some code:
public interface IFactory
{
Client CreateClient();
User CreateUser();
Channel CreateChannel(BasicHttpBinding binding, EndpointAddress endpoint);
}
abstract public class AbstractFactory<T> : IFactory
{
public abstract Client CreateClient()
public abstract User CreateUser();
public Channel CreateChannel(BasicHttpBinding binding, EndpointAddress endpoint)
{
var channelFactory = new ChannelFactory<T>(binding, endpoint);
return channelFactory.CreateChannel();
}
}
public class AWIFactory : AbstractFactory<WebServiceAWI>
{
public override Client CreateClient()
{
return new WebServiceAWIClient();
}
public override User CreateUser()
{
return new ArmUser();
}
}
public class WRGFactory : AbstractFactory<WebServiceWRG>
{
public override Client CreateClient()
{
return new WebServiceWRGClient();
}
public override User CreateUser()
{
return new User();
}
}
public class WebService
{
private readonly IFactory _factory;
public WebService(IFactory factory)
{
_factory = factory;
}
public async void ClaimSearchAsync(string url, string userName, string password)
{
var client = _factory.CreateClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channel = _factory.CreateChannel(binding, endpoint);
var user = _factory.CreateUser();
user.UserName = await channel.EncryptValueAsync(userName);
user.Password = await channel.EncryptValueAsync(password);
var response = await client.ClaimSearchAsync(user, "", "", 12345, statusCode, "");
}
...
}
And here is how you create WebService:
var wrgWebService = new WebService(new WRGFactory());
I did something similar when I had several different soap endpoints, where every endpoint had some types that were completely the same, only with a different class name. Automatically generated classes contain the partial modifier, which enables you to add additional logic to the generated class.
In your case:
"'TTwo' does not contain a definition for 'UserName'"
You have to create an interface that contains a property Username and a property Password:
public interface IUser {
string UserName { get; }
string Password { get; }
}
public partial User : IUser { } //must be in the correct namespace for partial to work
public partial ArmUser : IUser { } //must be in the correct namespace for partial to work
public class Test {
public void TestWebService() {
var ws = new WebService<WebServiceWRG>();
ws.SearchClaim(new WebServiceWRGClient(), new GraceUser(),
"https://trustonline.delawarecpf.com/tows/webservicewrg.svc", "userName", "password");
}
}
public class WebService<T> {
public void SearchClaim<TOne, TTwo>(TOne entity1, TTwo entity2, string url, string userName, string password)
where TOne : class
where TTwo : IUser // limits the TTwo class to implement IUser
{
var client = entity1;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = entity2;
user.UserName = webService.EncryptValue(userName);
user.Password = webService.EncryptValue(password);
var response = client.ClaimSearch(user, "", "", 12345, GraceStatuscode.NotSet, "");
}
}
Instead of passing TTwo, you can then also add the new modifier to the TTwo condition (where T : TTwo, new()), then you can generate an instance of TTwo inside the SearchClaim function, which would make it look like the following:
public interface IUser {
string UserName { get; set; }
string Password { get; set; }
}
public partial User : IUser { } //must be in the correct namespace for partial to work
public partial ArmUser : IUser { } //must be in the correct namespace for partial to work
public class Test {
public void TestWebService() {
var ws = new WebService<WebServiceWRG>();
ws.SearchClaim(new WebServiceWRGClient(), new GraceUser(),
"https://trustonline.delawarecpf.com/tows/webservicewrg.svc", "userName", "password");
}
}
public class WebService<T> {
public void SearchClaim<TOne, TTwo>(TOne entity1, string url, string userName, string password)
where TOne : class
where TTwo : IUser, new() // limits the TTwo class to implement IUser
{
var client = entity1;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new TTwo();
user.UserName = webService.EncryptValue(userName);
user.Password = webService.EncryptValue(password);
var response = client.ClaimSearch(user, "", "", 12345, GraceStatuscode.NotSet, "");
}
}
You might also have to make some interface for your TOne, but you should be able to figure that out on your own.
Not sure how is your code structure, but I'll just focus on the provided sample.
From what I've seen, if this method and the other related methods are used in different classes, I would suggest to create a class that will handle it, and then use this class instead of the methods. But if this method and other related methods are used in a specific class, I would recommend to create a generic methods that will substitute the redundant methods. You'll need to compare all related methods first, and get the common numerator between them, make this a start point for your generic approach.
Here is untested example (based on what I understood from your sample) :
public class CallWebService<T> // don't forget to inherit IDisposal.
{
private WebServiceWRGClient Client {get; set;}
private BasicHttpBinding HttpBinding {get; set;}
private EndpointAddress Endpoint {get; set;}
private ChannelFactory Channel {get; set;}
// if needed outside this class, make it public to be accessed globally.
private User UserAccount {get; set;}
public CallWebService<T>(string url)
{
Client = new WebServiceWRGClient();
//See which Binding is the default and use it in this constructor.
HttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
Endpoint = new EndpointAddress(url);
// T is generic, WebServiceWRG in this example
Channel = new ChannelFactory<T>(HttpBinding, Endpoint).CreateChannel();
UserAccount = new User();
}
// another constructor with BasicHttpBinding
public CallWebService<T>(string url, BasicHttpSecurityMode securityMode)
{
Client = new WebServiceWRGClient();
//See which Binding is the default and use it in this constructor.
HttpBinding = new BasicHttpBinding(securityMode);
Endpoint = new EndpointAddress(url);
// T is generic, WebServiceWRG in this example
Channel = new ChannelFactory<T>(HttpBinding, Endpoint).CreateChannel();
UserAccount = new User();
}
// Change this method to return the response. Task<Response> is just a placeholder for this example
public async Task<Response> Call(string userName, string password)
{
UserAccount.UserName = await Channel.EncryptValueAsync(userName);
UserAccount.Password = await Channel.EncryptValueAsync(password);
var response = await Client.ClaimSearchAsync(User, "", "", 12345, statuscode.NotSet, "");
}
/*
[To-Do] : gather all other releated methods into this class, then try to simplify them.
*/
}
You can also configure the constructors as needed, for instance, you can make constructors that takes WebServiceWRGClient and BasicHttpBinding ..etc. So, it's more open to you.
You could do similar approach if it'll be used across the project, but if it's only used in one class, then you could do something like this :
// Configure it as needed, but avoid using `void` with async, as the exceptions in sync and async methods handled differently.
// Also, try to make sense here, make the method return the results.
public async Task CallWebService<T>(WebServiceWRGClient client, string url, string userName, string password)
{
var channelFactory = new ChannelFactory<T>(new BasicHttpBinding(BasicHttpSecurityMode.Transport, new EndpointAddress(url)).CreateChannel();
var user = new User(); // coming from service reference
user.UserName = await channelFactory.EncryptValueAsync(userName);
user.Password = await channelFactory.EncryptValueAsync(password);
var response = await client.ClaimSearchAsync(user, "", "", 12345, statuscode.NotSet, "");
}

How can I use proxies for web requests in Flurl?

I have a simple post request using the Flurl client, and I was wondering how to make this request using a proxy using information like the IP, port, username, and password.
string result = await atc.Request(url)
.WithHeader("Accept", "application/json")
.WithHeader("Content-Type", "application/x-www-form-urlencoded")
.WithHeader("Host", "www.website.com")
.WithHeader("Origin", "http://www.website.com")
.PostUrlEncodedAsync(new { st = colorID, s = sizeID, qty = 1 })
.ReceiveString();
I was looking for a similar answer and found this:
https://github.com/tmenier/Flurl/issues/228
Here is a copy of the contents of that link. It worked for me!
You can do this with a custom factory:
using Flurl.Http.Configuration;
public class ProxyHttpClientFactory : DefaultHttpClientFactory {
private string _address;
public ProxyHttpClientFactory(string address) {
_address = address;
}
public override HttpMessageHandler CreateMessageHandler() {
return new HttpClientHandler {
Proxy = new WebProxy(_address),
UseProxy = true
};
}
}
To register it globally on startup:
FlurlHttp.Configure(settings => {
settings.HttpClientFactory = new ProxyHttpClientFactory("http://myproxyserver");
});

Authenticate users in Asp .net Web API

I'm writing API which will be consumed by mobile devices and I want to secure this API end points.
User authentication details is provide by another application called User Manger API (another project which contains user details).
How to make use of ASP.NET Identity framework Authorization and other features to secure my API endpoints while getting the user data from the User manager API ?
The question is a bit broad; basically you are looking for a strategy to authenticate and authorise a client for a web api (dotnet core or normal framework?) using a different existing API (is that API in your control, can you modify it if needed?)
If you can modify both, id say look through StackOverflow and Google for JWT tokens, OAuth and identity server.
1- you can implement an attribute and decorate your api controller.
2- you can implement and register a global filter inside your asp.net's app_start (and make sure you are registering filters in your global.asax).
3- you can do what #Roel-Abspoel mentions implement Identity Server in your User Manager API and have your client talk to it and get the token, then your API talk to it to validate the token.
There are other ways, but i will keep this short and sweet.
Here is an example using an attribute:
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Filters;
namespace myExample
{
public class ExternalAuthenticationAttribute : IAuthenticationFilter
{
public virtual bool AllowMultiple
{
get { return false; }
}
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// get request + authorization headers
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// check for username and password (regardless if it was validated on the client, server should check)
// this will only accept Basic Authorization
if (String.IsNullOrEmpty(authorization.Parameter) || authorization.Scheme != "Basic")
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return null;
}
var userNameAndPasword = GetCredentials(authorization.Parameter);
if (userNameAndPasword == null)
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Could not get credentials", request);
return null;
}
// now that we have the username + password call User manager API
var client = new HttpClient();
// POST USERNAME + PASSWORD INSIDE BODY, not header, not query string. ALSO USE HTTPS to make sure it is sent encrypted
var response = AuthenticateAgainstUserMapagerApi1(userNameAndPasword, client);
// THIS WILL WORK IN .NET CORE 1.1. ALSO USE HTTPS to make sure it is sent encrypted
//var response = AuthenticateAgainstUserMapagerApi2(client, userNameAndPasword);
// parse response
if (!response.IsSuccessStatusCode)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
else
{
var readTask = response.Content.ReadAsStringAsync();
var content = readTask.Result;
context.Principal = GetPrincipal(content); // if User manager API returns a user principal as JSON we would
}
return null;
}
//private static HttpResponseMessage AuthenticateAgainstUserMapagerApi2(HttpClient client, Tuple<string, string> userNameAndPasword)
//{
// client.SetBasicAuthentication(userNameAndPasword.Item1, userNameAndPasword.Item2);
// var responseTask = client.GetAsync("https://your_user_manager_api_URL/api/authenticate");
// return responseTask.Result;
//}
private static HttpResponseMessage AuthenticateAgainstUserMapagerApi1(Tuple<string, string> userNameAndPasword, HttpClient client)
{
var credentials = new
{
Username = userNameAndPasword.Item1,
Password = userNameAndPasword.Item2
};
var responseTask = client.PostAsJsonAsync("https://your_user_manager_api_URL/api/authenticate", credentials);
var response = responseTask.Result;
return response;
}
public IPrincipal GetPrincipal(string principalStr)
{
// deserialize principalStr and return a proper Principal instead of ClaimsPrincipal below
return new ClaimsPrincipal();
}
private static Tuple<string, string> GetCredentials(string authorizationParameter)
{
byte[] credentialBytes;
try
{
credentialBytes = Convert.FromBase64String(authorizationParameter);
}
catch (FormatException)
{
return null;
}
try
{
// make sure you use the proper encoding which match client
var encoding = Encoding.ASCII;
string decodedCredentials;
decodedCredentials = encoding.GetString(credentialBytes);
int colonIndex = decodedCredentials.IndexOf(':');
string userName = decodedCredentials.Substring(0, colonIndex);
string password = decodedCredentials.Substring(colonIndex + 1);
return new Tuple<string, string>(userName, password);
}
catch (Exception ex)
{
return null;
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
public class AuthenticationFailureResult : IHttpActionResult
{
public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
{
ReasonPhrase = reasonPhrase;
Request = request;
}
public string ReasonPhrase { get; private set; }
public HttpRequestMessage Request { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
private HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = Request;
response.ReasonPhrase = ReasonPhrase;
return response;
}
}
}
use the attribute on your API class like this, which will call User Manager API each time PurchaseController is accessed:
[ExternalAuthenticationAttribute]
public class PurchaseController : ApiController

ServiceStack ServerSentEvents restrict access to channel

In my ServiceStack app I would like to deny access to channels for unauthorized users - so even the join event would not fire for an unauthorized client. I am using custom auth provider that does not interact with the DB and is very minimalistic for now (mainly for testing purposes)
public class RoomsAuthProvider : CredentialsAuthProvider
{
private int userId = 0;
public RoomsAuthProvider(AppSettings appSettings) : base(appSettings)
{
}
public RoomsAuthProvider()
{
}
public override bool TryAuthenticate(IServiceBase authService,
string userName, string password)
{
if (password == "ValidPassword")
{
return true;
}
else
{
return false;
}
}
public override IHttpResult OnAuthenticated(IServiceBase authService,
IAuthSession session, IAuthTokens tokens,
Dictionary<string, string> authInfo)
{
//Fill IAuthSession with data you want to retrieve in the app eg:
session.FirstName = "some_firstname_from_db";
//...
//Call base method to Save Session and fire Auth/Session callbacks:
return base.OnAuthenticated(authService, session, tokens, authInfo);
//session.CreatedAt = DateTime.Now;
//session.DisplayName = "CustomDisplayName" + userId;
//session.IsAuthenticated = true;
//session.UserAuthName = session.UserName;
//session.UserAuthId = userId.ToString();
//Interlocked.Increment(ref userId);
//authService.SaveSession(session, SessionExpiry);
//return null;
}
}
Main service piece:
[Authenticate]
public class ServerEventsService : Service
{
...
}
sidenote - I have tried overriding the default DisplayUsername to not be username1...usernameN but no luck. My client code is
var client = new ServerEventsClient("http://localhost:1337/", "home")
{
OnConnect = OnConnect,
OnCommand = HandleIncomingCommand,
OnMessage = HandleIncomingMessage,
OnException = OnException,
OnHeartbeat = OnHeartbeat
}.Start();
client.Connect().Wait();
var authResponse = client.Authenticate(new Authenticate
{
provider = "credentials",
UserName = "test#gmail.com",
Password = "p#55w0rd",
RememberMe = true,
});
client.ServiceClient.Post(new PostChatToChannel
{
Channel = "home", // The channel we're listening on
From = client.SubscriptionId, // Populated after Connect()
Message = "Hello, World!",
});
Even if I skip the authenticate call the other clients will still get onJoin command about not authenticated client when it tries to do an unauthorized post (and get an error). Also when I intentionally do multiple unauthorized users counter grows - assigned username becomes username2, username3 and so on - how can I disable unauthorized users COMPLETELY? Marking my DTOs with Authenticate also didn't change anything. Any ideas are welcome as well as crytics as I'm new to ServiceStack and would like to implement the best practices.
There's already an option to limit access to authenticated users only with:
Plugins.Add(new ServerEventsFeature {
LimitToAuthenticatedUsers = true
});

How to resolve component based on user context using Autofac

I have a service that requires a UserName to be passed in during construction. The UserName will come from the current web request either cookie or query string.
builder.Register((c,p) => {
var accessService = c.Resolve<IAccessService>();
var access = accessService.GetBySite(Request.QueryString["username"]);
return new JsonWebRequest(access.Site, access.Token);
}).InstancePerRequest();
I've tried to register as above although I receive this error message
Request is not available in this context
You should use HttpContext.Current to access the information from the active context.
builder.Register((c,p) => {
var accessService = c.Resolve<IAccessService>();
var request = HttpContext.Current.Request;
var access = accessService.GetBySite(request.QueryString["username"]);
return new JsonWebRequest(access.Site, access.Token);
}).InstancePerRequest();
Another way of doing it is by using the AutofacWebTypesModule which will import registration for HttpRequestBase. This module is available using the Autofac MVC5 nuget package
builder.RegisterModule<AutofacWebTypesModule>();
builder.Register((c,p) => {
var accessService = c.Resolve<IAccessService>();
var request = c.Resolve<HttpRequestBase>();
var access = accessService.GetBySite(request.QueryString["username"]);
return new JsonWebRequest(access.Site, access.Token);
}).InstancePerRequest();
By the way, for testing purpose and more flexibility I would recommend you to create a IUserNameProvider interface that will get you a username
public interface IUserNameProvider
{
String UserName { get; }
}
public class QueryStringUserNameProvider
{
public QueryStringUserNameProvider(HttpRequestBase request)
{
this._request = request;
}
private readonly HttpRequestBase _request;
public String UserName
{
get
{
return this._request.QueryString["UserName"];
}
}
}
You can use it like this :
builder.RegisterModule<AutofacWebTypesModule>();
builder.RegisterType<QueryStringUserNameProvider>()
.As<IUserNameProvider>()
.InstancePerRequest();
builder.Register((c,p) => {
var accessService = c.Resolve<IAccessService>();
var userNameProvider = c.Resolve<IUserNameProvider>();
var access = accessService.GetBySite(userNameProvider.UserName);
return new JsonWebRequest(access.Site, access.Token);
}).InstancePerRequest();

Categories