How to test Web.API method with RestSharp passing in ClaimsPrincipal - unit-testing

I'm having a bit of trouble with a specific implementation of testing out my Web.API methods using RestSharp. I have been very successful in performing POSTS and GETS in my open (non-secured) methods. However, when I have to send in a token to determine access I have problems.
Here is the implementation:
I am using OWIN middleware for my Web.API. The client must post to a token service in order to get the given Token that contains their claims. All of this has been working fine.
In my test my Initializer has the following code that posts to the token service and gets back the token. This works wonderfully - returns back the token as advertised:
[TestInitialize]
public void SetupTest()
{
_verificationErrors = new StringBuilder();
_client = new RestClient
{
BaseUrl = new Uri(ConfigurationManager.AppSettings["ServicesBaseUrl"])
};
_serviceRequestPrepender = ConfigurationManager.AppSettings["ServiceRequestPrepender"];
// Initialize this by getting the user token put back for all of the tests to use.
var request = new RestRequest(string.Format("{0}{1}", _serviceRequestPrepender, ConfigurationManager.AppSettings["TokenEndpointPath"]), Method.POST);
// Add header stuff
request.AddParameter("Content-Type", "application/x-www-form-urlencoded", ParameterType.HttpHeader);
request.AddParameter("Accept", "application/json", ParameterType.HttpHeader);
// Add request body
_userName = "{test student name}";
_password = "{test student password}";
_userGuid = "{this is a guid value!!}";
_clientIdentifier = ConfigurationManager.AppSettings["ClientIdentifier"];
_applicationId = ConfigurationManager.AppSettings["ApplicationId"];
string encodedBody = string.Format("grant_type=password&username={0}&password={1}&scope={2} {3} {4} {0}"
, _userName, _password, _clientIdentifier, _userGuid, _applicationId);
request.AddParameter("application/x-www-form-urlencoded", encodedBody, ParameterType.RequestBody);
// execute the request
IRestResponse response = _client.Execute(request);
// Make sure everything is working as promised.
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsTrue(response.ContentLength > 0);
_token = new JavaScriptSerializer().Deserialize<Token>(response.Content).access_token;
}
Next is the following code that calls a Web.API method which passes the given token along to another Web.API method where I'm performing a GET to extract some information from my service.
[TestMethod]
public void GetUserProfileTest()
{
// Arrange
var request = new RestRequest(string.Format("{0}{1}", _serviceRequestPrepender, "api/UserProfiles/UserProfiles/Get/{appId}/{userId}/{username}"), Method.GET);
// Add header stuff
request.AddParameter("Content-Type", "application/json", ParameterType.HttpHeader);
request.AddParameter("Accept", "/application/json", ParameterType.HttpHeader);
request.AddParameter("Authorization", string.Format("{0} {1}", "Bearer", _token));
request.AddUrlSegment("appId", "1");
request.AddUrlSegment("userId", _userGuid);
request.AddUrlSegment("username", _userName);
// execute the request
IRestResponse response = _client.Execute(request);
// Make sure everything is working as promised.
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
Assert.IsTrue(response.ContentLength > 0); // do more when working
}
Next, the service is called, but I have decorated the Web.API method with a custom access security check. This is a VERY simple security check in that it only checks to see if the token is valid and not expired. Here is the IsAuthorized method of that attribute:
protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// Custom Code here
return ValidityChecker.IsTokenValid(actionContext);
}
The ValidityChecker is a simple class that only checks to see if the token is valid:
public class TokenValidityChecker
{
public ClaimsPrincipal PrincipalWithClaims { get; private set; }
/// <summary>
/// Extracts out the ability to perform token checking since all Token checking attributes will need t his.
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
public bool IsTokenValid(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool result = false;
var principal = actionContext.RequestContext.Principal;
if (principal.GetType() == typeof(ClaimsPrincipal))
{
PrincipalWithClaims = (ClaimsPrincipal)principal;
result = PrincipalWithClaims.Identity.IsAuthenticated;
}
// Custom Code here
return result;
}
}
So, with the background in place - here is the question. As you can see, normally, when the service is called the ValidityChecker will receive an HttpActionContext. Along with that, the RequestContext.Principal of that HttpActionContext will normally be of type ClaimsPrincipal.
However, when running from a unit test and using RestSharp it is, of course, a WindowsPrincipal.
Is there a way using RestSharp to make that a ClaimsPrincipal? I've tried to ensure the token is included in the header using the Authorization parameter, but have not had any luck.

Well - If I would simply read the details of my own code I could have completed this long ago.
The answer was VERY simple. The code in the question adds the token to the parameters, but does not annotate it as HttpHeader. I forgot to put that into the method call. Here is the line that fixed it:
request.AddParameter("Authorization", string.Format("{0} {1}", "Bearer", _token), ParameterType.HttpHeader);
The "ParameterType.HttpHeader" in the method call did the trick.

Related

Junit Unable to return mocked Response and return as null

I m new to Mockito and trying to mock the webservice responses, I did tried mocking at some extent few Objects got worked, But the end mocked WebResponse is always returning null.
Service Method i am going to test:getWebResponse Method
public WebResponse getWebResponse(String crmNumber) throws JSONException, ExecutionException, WebException {
Map<String, String> HEADERS_POST = new HashMap<String, String>() {
{
put(WebUtil.HEADER_CONTENT, WebUtil.CONTENT_JSON);
put(WebUtil.HEADER_ACCEPT, WebUtil.CONTENT_JSON);
}
};
JSONObject requestJson = new JSONObject();
requestJson.put("crmNumber", crmNumber);
requestJson.put("application", "ABCD");
requestJson.put("feature", "DDDFL");
// Using internal web service becuase device authentication is done separately.
String url = CommonUtil.getServiceBaseUrl(true) + "/ett";
WebServiceClient client = WebServiceClientRegistry.getClient(ApacheCustom.class);
WebRequest webReq = new GenericWebRequest(WebRequestMethod.POST, url, HEADERS_POST, requestJson.toString());
// Till here i m getting all mocked object (client also Mocked) after this stament the webRes is returning null;
WebResponse webRes = client.doRequest(webReq);
return webRes;
}
And here the test Method:
#Test
public void getWebResponseTest() {
mockStatic(CommonUtil.class);
mockStatic(WebServiceClientRegistry.class);
this.webResponse = new GenericWebResponse(200, "", new HashMap(), "");
try {
Mockito.when(CommonUtil.getServiceBaseUrl(true)).thenReturn("https://stage.com/service");
WebRequest webReq = new GenericWebRequest(WebRequestMethod.POST, "https://stage.com/service", new HashMap(), "");
Mockito.when(WebServiceClientRegistry.getClient(ApacheCustom.class)).thenReturn(client);
Mockito.when(client.doRequest(webReq)).thenReturn(this.webResponse);
WebResponse wesponse = this.ServiceResponse.getWebResponse("Number");
Assert.assertEquals(wesponse.getStatusCode(), 200);
} catch (Exception e) {
Assert.fail();
}
}
But the getWebResonse method from Test class always returning null Response(Even Though it is mocked)
You mock client.doRequest as follows:
Mockito.when(client.doRequest(webReq)).thenReturn(this.webResponse);
but you create a new instance of WebRequest in your service under test.
You call doRequest with a different argument than recorded in your test.
Arguments are compared with equals.
Most likely WebRequest does not override equals, so recorded interaction is ignored and a default response (null) is rerurned.
I guess WebResuest may not be the code you own (you haven’t specified this in your question), so it may be impossible to override it.
Thus, you can use a different argument matcher.
You can use ArgumentMatchers.any() for good start, or implement a custom argument matcher.

Unit Testing - Spring Boot App

I looked at this link : How to write a unit test for a Spring Boot Controller endpoint
I am planning to unit test my Spring Boot Controller. I have pasted a method from my controller below. When I use the approach mentioned in the link above , will the call that I have to service.verifyAccount(request) not be made? Are we just testing whether the controller accepts the request in format specified and returns response in format specfied apart from testing the HTTP status codes?
#RequestMapping(value ="verifyAccount", method = RequestMethod.POST, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<VerifyAccountResponse> verifyAccount(#RequestBody VerifyAccountRequest request) {
VerifyAccountResponse response = service.verifyAccount(request);
return new ResponseEntity<VerifyAccountResponse>(response, HttpStatus.OK);
}
You can write unit test case
using
#RunWith(SpringJUnit4ClassRunner.class)
// Your spring configuration class containing the
#EnableAutoConfiguration
// annotation
#SpringApplicationConfiguration(classes = Application.class)
// Makes sure the application starts at a random free port, caches it throughout
// all unit tests, and closes it again at the end.
#IntegrationTest("server.port:0")
#WebAppConfiguration
make sure you configure all your server configuration like port/url
#Value("${local.server.port}")
private int port;
private String getBaseUrl() {
return "http://localhost:" + port + "/";
}
Then use code mentioned below
protected <T> ResponseEntity<T> getResponseEntity(final String
requestMappingUrl, final Class<T> serviceReturnTypeClass, final Map<String, ?>
parametersInOrderOfAppearance) {
// Make a rest template do do the service call
final TestRestTemplate restTemplate = new TestRestTemplate();
// Add correct headers, none for this example
final HttpEntity<String> requestEntity = new HttpEntity<String>(new
HttpHeaders());
// Do a call the the url
final ResponseEntity<T> entity = restTemplate.exchange(getBaseUrl() +
requestMappingUrl, HttpMethod.GET, requestEntity, serviceReturnTypeClass,
parametersInOrderOfAppearance);
// Return result
return entity;
}
#Test
public void getWelcomePage() {
Map<String, Object> urlVariables = new HashMap<String, Object>();
ResponseEntity<String> response = getResponseEntity("/index",
String.class,urlVariables);
assertTrue(response.getStatusCode().equals(HttpStatus.OK));
}

How to mock a melthod with out paramter in Moq AND fill the out

I have see several questions here in Stackoverflow about out parameters in MOQ, my question is how fill this parameter: Lets to code:
[HttpPost]
public HttpResponseMessage Send(SmsMoRequest sms)
{
if (sms == null)
return Request.CreateResponse(HttpStatusCode.BadRequest);
SmsMoResponse response;
_messageService.Process(sms, out response);
return Request.CreateResponse(HttpStatusCode.Created, response.ToString());
}
I want to test this post:
[Test]
public void Should_Status_Be_Create_With_Valid_XML()
{
// Arrange
var messageServiceMoq = new Mock<IMessageService>();
SmsMoResponse response;
messageServiceMoq.Setup(mock => mock.Process(It.IsNotNull<SmsMoRequest>(), out response));
_kernel.Bind<IMessageService>().ToConstant(messageServiceMoq.Object);
var client = new HttpClient(_httpServer) { BaseAddress = new Uri(Url) };
// Act
using (var response = client.PostAsync(string.Format("Api/Messages/Send"), ValidContent()).Result)
{
// Asserts
response.IsSuccessStatusCode.Should().BeTrue();
response.StatusCode.Should().Be(HttpStatusCode.Created);
}
}
Problem
My response object in Send method (POST) is used in post response but the _messageService.Process is responsible to fill the response object.
In test method Should_Status_Be_Create_With_Valid_XML I mock _messageService.Process and response object is not fill ocorr error Null reference in Request.CreateResponse(HttpStatusCode.Created, response.ToString());
response is null!
Of course response is null, there's no code anywhere that would set it to anything (in your service method or your mock).
Since you're mocking the method that would usually fill it in, it's up to you to specify how it is set. You should fill in the object before calling Setup with what you expect to be the value when that method is called.
Also, see this question for more info.

OAuth making requests against LinkedIn API cause 401 error

I am creating an application for a client that integrates with the LinkedIn API. I got through the authentication without too many problems, but everything there is working and now I need to make the actual requests. Primarily I am working in the Share API. I create the HTTP call with the following method:
public any function sendRequest(any req){
var param = false;
var headParams = [];
var bodyParams = [];
var call = new http(proxyserver='192.168.201.12', proxyport=8888);
var i = 1;
call.setUrl(Arguments.req.getRequestUrl());
call.setMethod(Arguments.req.getMethod());
getSigner().signRequest(Arguments.req);
headParams = Arguments.req.getParameters(true);
bodyParams = Arguments.req.getParameters();
if(arrayLen(bodyParams)){
call.addParam(
type='header',
name='Authorization',
value="OAuth#Variables.encoder.encodedParameter(Arguments.req.getParameters(true), true, false, true)#"
);
}
// Header parameters
if(!arrayLen(bodyParams)){
for(i=1; i<=arrayLen(headParams); i++){
param = headParams[i];
call.addParam(
type=Arguments.req.getParameterType(),
name=Variables.encoder.parameterEncodedFormat(param.name),
value=param.value
);
}
}
// Body parameters (should only be 1)
if(arrayLen(bodyParams)){
for(i=1; i<=arrayLen(bodyParams); i++){
param = bodyParams[i];
call.addParam(
type='xml',
value=param.value
);
}
}
return call.send().getPrefix();
}
When I sign the request, I use the following method:
public void function signRequest(any req){
var headParams = Arguments.req.getParameters(true);
var bodyParams = Arguments.req.getParameters();
var secret = "#Variables.encoder.parameterEncodedFormat(getConsumer().getConsumerSecret())#&#Variables.encoder.parameterEncodedFormat(Arguments.req.getOAuthSecret())#";
var base = '';
params = Variables.encoder.encodedParameter(headParams, true, true);
params = "#params#&#Variables.encoder.parameterEncodedFormat(bodyParams[1].value)#";
secret = toBinary(toBase64(secret));
local.mac = createObject('java', 'javax.crypto.Mac').getInstance('HmacSHA1');
local.key = createObject('java', 'javax.crypto.spec.SecretKeySpec').init(secret, local.mac.getAlgorithm());
base = "#Arguments.req.getMethod()#&";
base = base & Variables.encoder.parameterEncodedFormat(Arguments.req.getRequestUrl());
base = "#base#&#Variables.encoder.parameterEncodedFormat(params)#";
//writeDump(base) abort;
local.mac.init(local.key);
local.mac.update(JavaCast('string', base).getBytes());
Arguments.req.addParameter('oauth_signature', toString(toBase64(mac.doFinal())), true);
}
I have tried signing it with only the header parameters (usual OAuth params) and include the body parameter (xml string), but everything gives me a 401 error, so I was wondering what I should be using in my base string that gets signed for the request?
Not a proper answer to your question, but may help you.
In my case after many unsuccessful tries of using the LinkedIn API with CF8, I finally gave up / didn't have more time for it. Instead of a "proper" integration I've used the linkedin-j Java library. It finally got me going and I didn't encounter any more signing issues.
Btw for all my integrations requiring OAuth I've used this library and didn't have any signing issues as with LinkedIn API.

openid4java ConsumerManager request/thread safe?

I am using openid4java in servlets. I have two servlets - one which performs first step (redirects user to login/accept application access) and second, which processes resulting information
In the documentation, there is written, that org.openid4java.consumer.ConsumerManager class must be the same instance in both steps. Can I create singleton for that? Is it thread and request safe?
Thanks for your replies!
In the consumer servlet from official openid4java sample it seems that ConsumerManager is thread safe - they use single ConsumerManager instance for all sessions. I use it this way too and have not noticed any strange behaviour yet. But a javadoc statement about thread-safety from the developers would be great...
//Currently only working with google only
// Try this - this is all ine one..
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//import org.jboss.web.tomcat.security.login.WebAuthentication;
import org.openid4java.OpenIDException;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.ParameterList;
import org.openid4java.message.ax.AxMessage;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.message.ax.FetchResponse;
public class OpenAuth extends javax.servlet.http.HttpServlet {
final static String YAHOO_ENDPOINT = "https://me.yahoo.com";
final static String GOOGLE_ENDPOINT = "https://www.google.com/
accounts/o8/id";
//Updated version of example code from :
https://crisdev.wordpress.com/2011/03/23/openid4java-login-example/
//Add your servlet script path here - so if auth fails or
succeeds it will carry out actions - check below in doGet
public String scr="/servlets/MyServlet";
private ServletContext context;
private ConsumerManager manager;
private ConsumerManager mag;
//Code updated by Vahid Hedayati http://pro.org.uk
//Removed config init - moved post to doGet - since previous code
required it to be a post but also to include identifier as part of
url
//identifier was also the same variable used for Identifier code -
//cleaned up to make different variable and less confusion
//doGet identifer changed to openid_identifier and it also now looks
for openid_username which are the default variables returned from
openid-selector
//http://groups.google.com/group/openid4java/browse_thread/thread/
5e8f24f51f54dc2c
//After reading above post - store the manager in the session object
and failing with Yahoo authentication I changed code for the manager
//manage
public void doPost(HttpServletRequest req,HttpServletResponse
response) throws ServletException,IOException {
doGet(req, response);
}
protected void doGet(HttpServletRequest req, HttpServletResponse
resp) throws ServletException, IOException {
//New variable
String ouser=(String)req.getParameter("openid_username");
if (ouser==null) { ouser="";}
//Mage is the session value of openid_consumer_manager if it is
null it will generate it once
//And where ever manager is called within code it first returns
managers value by looking up session value
mag=(ConsumerManager)req.getSession().getAttribute("open_id_consumer_manager");
if (mag==null) {
this.manager = new ConsumerManager();
req.getSession().setAttribute("open_id_consumer_manager", manager);
}
String identify=(String)req.getParameter("openid_identifier");
if (identify==null) { identify="";}
if (!identify.equals("")) {
this.authRequest(identify,ouser, req, resp);
}else{
//If they have succeeded it will return them to welcome
//welcome looks up if NEWUSER = yes in the session value below
and if so
//scr now has the ip city/country/postcode so it finalises
user additiion by adding users ip country/city/ip as their sign up
// if not new well they are already logged in from the
relevant session values this code has put in so updats records and
returns they my accoount
//if authentication here failed or they rejected sharing their
email then login page is returned
Identifier identifier = this.verifyResponse(req);
if (identifier != null) {
resp.sendRedirect(scr+"?act=welcome");
} else {
resp.sendRedirect(scr+"?act=login");
}
}
}
// --- placing the authentication request ---
public String authRequest(String userSuppliedString,String Ouser,
HttpServletRequest httpReq, HttpServletResponse httpResp) throws
IOException {
try {
// configure the return_to URL where your application will
receive
// the authentication responses from the OpenID provider
String returnToUrl = httpReq.getRequestURL().toString();
// --- Forward proxy setup (only if needed) ---
// ProxyProperties proxyProps = new ProxyProperties();
// proxyProps.setProxyName("proxy.example.com");
// proxyProps.setProxyPort(8080);
// HttpClientFactory.setProxyProperties(proxyProps);
// perform discovery on the user-supplied identifier
//Modified - Look up manager value from session
manager = (ConsumerManager)
httpReq.getSession().getAttribute("open_id_consumer_manager");
List discoveries = manager.discover(userSuppliedString);
// attempt to associate with the OpenID provider
// and retrieve one service endpoint for authentication
DiscoveryInformation discovered =
manager.associate(discoveries);
// store the discovery information in the user's session
httpReq.getSession().setAttribute("openid-disc", discovered);
// obtain a AuthRequest message to be sent to the OpenID
provider
AuthRequest authReq = manager.authenticate(discovered,
returnToUrl);
FetchRequest fetch = FetchRequest.createFetchRequest();
if (userSuppliedString.startsWith(GOOGLE_ENDPOINT)) {
fetch.addAttribute("email", "http://axschema.org/
contact/email", true);
fetch.addAttribute("firstName", "http://axschema.org/
namePerson/first", true);
fetch.addAttribute("lastName", "http://axschema.org/
namePerson/last", true);
} else if (userSuppliedString.startsWith(YAHOO_ENDPOINT)) {
fetch.addAttribute("email", "http://axschema.org/
contact/email", true);
fetch.addAttribute("fullname", "http://axschema.org/
namePerson", true);
} else {
// works for myOpenID
fetch.addAttribute("fullname", "http://
schema.openid.net/namePerson", true);
fetch.addAttribute("email", "http://schema.openid.net/
contact/email", true);
}
httpReq.getSession().setAttribute("Ouser",Ouser);
// attach the extension to the authentication request
authReq.addExtension(fetch);
httpResp.sendRedirect(authReq.getDestinationUrl(true));
} catch (OpenIDException e) {
// present error to the user
}
return null;
}
// --- processing the authentication response ---
public Identifier verifyResponse(HttpServletRequest httpReq) {
try {
// extract the parameters from the authentication response
// (which comes in as a HTTP request from the OpenID provider)
ParameterList response = new
ParameterList(httpReq.getParameterMap());
// retrieve the previously stored discovery information
DiscoveryInformation discovered = (DiscoveryInformation)
httpReq.getSession().getAttribute("openid-disc");
// extract the receiving URL from the HTTP request
StringBuffer receivingURL = httpReq.getRequestURL();
String queryString = httpReq.getQueryString();
if (queryString != null && queryString.length() > 0)
receivingURL.append("?").append(httpReq.getQueryString());
// verify the response; ConsumerManager needs to be the same
// (static) instance used to place the authentication request
//Modified - look up session value before running verification
result
manager = (ConsumerManager)
httpReq.getSession().getAttribute("open_id_consumer_manager");
VerificationResult verification =
manager.verify(receivingURL.toString(), response, discovered);
// examine the verification result and extract the verified
// identifier
Identifier verified = verification.getVerifiedId();
String id=verified.getIdentifier();
if (verified != null) {
AuthSuccess authSuccess = (AuthSuccess)
verification.getAuthResponse();
if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
FetchResponse fetchResp = (FetchResponse)
authSuccess.getExtension(AxMessage.OPENID_NS_AX);
List emails =
fetchResp.getAttributeValues("email");
String email = (String) emails.get(0);
////////////////////////////////////////////////////////////////////////////////
//Custom bit each person needs to implement to
interact with their application:
//Authenticate the user, send email verify if
user exists on local system
//If it does {
//
httpReq.getSession().setAttribute("USERNAME",usern);
httpReq.getSession().setAttribute("LOGGEDIN", "on");
//}else{
String firstName =
fetchResp.getAttributeValue("firstName");
String lastName =
fetchResp.getAttributeValue("lastName");
String
fullname=fetchResp.getAttributeValue("fullname");
if (fullname==null)
{fullname="";}
if (firstName==null)
{ firstName="";}
if (lastName==null) { lastName="";}
if (!fullname.equals("")) {
if (fullname.indexOf(",")>-1)
{
firstName=fullname.substring(0,fullname.indexOf(","));
lastName=fullname.substring(fullname.indexOf(","),fullname.length());
}else if (fullname.indexOf("
")>-1){
firstName=fullname.substring(0,fullname.indexOf(" "));
lastName=fullname.substring(fullname.indexOf(" "),fullname.length());
}
}
//This is username returned
from the various services that ask for a username - it is returned as
openid_username
//When using openid-selector it uses
openid_identifier and openid_username - which is what this program now
looks for
String
ouser=(String)httpReq.getSession().getValue("Ouser");
if (ouser==null) {ouser="";}
//Adduser -- pass email address and
ouser
//In Adduser class - if ouser is blank
split email from 0 to substring.indexOf("#")
// generate a random number - look up
current user - if exist add random number to end
//and add user with email and new
username
//return bac the newuser and log in
like above.
httpReq.getSession().setAttribute("NEWUSER","YES");
//
httpReq.getSession().setAttribute("USERNAME",usern);
httpReq.getSession().setAttribute("LOGGEDIN", "on");
//}
return verified; // success
}
}
} catch (OpenIDException e) {
// present error to the user
}
return null;
}