I'm trying to test a controller made with Symfony 4 with PHPUnit.
I'm using https://github.com/lexik/LexikJWTAuthenticationBundle to manage JWT.
This controllers should return a 200 if a valid JWT is given, a 401/403 otherwise.
The first part with the 401 response is easy: I just don't send any token
<?php
namespace App\Tests\Controller;
Class GraphQLControllerTest extends \Symfony\Bundle\FrameworkBundle\Test\WebTestCase {
function test_token(){
$client = static::createClient();
$client->request('GET', '/graphql');
// Check 401 response
$response = $client->getResponse();
$this->assertSame(401, $response->getStatusCode());
$this->assertSame('"Not authenticated"', $response->getContent());
}
}
The next part is tricky: how do I get my JWT encoder service in my test_token method in order to generate some tokens to test 200 and 403 responses?
If I were in a controller I could use Symfony autowiring or make a public alias of lexik_jwt_authentication.encoder to be used like this:
$this->container->get('lexik_jwt_authentication.encoder')
Loading manually the service in my test like this bellow seems inefficient as some of the arguments of the constructor are some objects, and some arguments of their own constructor are objects, and ...
new Lexik\Bundle\JWTAuthenticationBundle\Encoder\DefaultEncoder([some objects here])
This is what is nice with autowiring: you just get the service you want ready to be used.
=> What's the best way to get this service in my test?
Thanks!
It's now possible with Symfony 4.1 to load a private service within a test using the KernelTestCase
Related
I have one service and Keycloak 11 as Authentication server. Now I want to write tests. To mock the accesstoken, I use #WithMockKeycloakAuth. This works well and I get an unauthorized when I pass a bad role for example. Now I want to document it with spring rest docs the therefor I have to add the acesstoken as header field ( Bearer tokenAsBearerString ). Because of the annotation, the mocked token is added to the SecurityContext and I can extract it before doing the mvc.perform.
#Test
#Order(5)
#WithMockKeycloakAuth(authorities = "ROLE_owner")
void createProduct_RealmRoleOwner_HttpStatusCreated() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
KeycloakAuthenticationToken authentication =(KeycloakAuthenticationToken) context.getAuthentication();
AccessToken token = authentication.getAccount().getKeycloakSecurityContext().getToken();
The problem is that I need the accesstoken as Bearer string representation. I'm not yet very familiar with the jwt topic but I expected that if I use the mocked acces token and convert it to a jwt format / Base 64 encoded String the header should be correct.
In addition: I'm running a Keycloak container via docker in a seperate network so it is not reachable while I run my automated test. So mocking would be the only solution.
This question was also asked (and answered) here with a little more context.
The code snippet provided above doesn't show that test class is decorated with #AutoConfigureMockMvc(addFilters = false), reason why the security context is not attached to the MockMvc HTTP request (this is normally done by a spring-security filter).
The complete stack trace isn't provided neither, but it's very likely that the exception occurring when filters are enabled is due to JwtDecoder wiring from Keycloak boot lib. #MockBean JwtDecoder jwtDecoder; should be enough to fix it.
Finally, it is one of main features of the lib #WithMockKeycloakAuth is taken from to skip fetching, decoding and parsing an actual JWT from Keycloak instance. Trying to build an authorization header with valid JWT from mocked spring authentication is ...
I am trying to add a grpc protofile to my swagger-ui. I am consuming a grpc webservice which needs a protofile as input. The input to my spring boot restful webservice needs to have that same grpc structure as its interface. I recevied a jar from the individual that made the protofile and imported it to my webserivce. When I try to add the #ResponseBody tag around the object from the protofile jar, my app hangs on this in the console at startup:
s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references
Thanks,
Brian
Never return entity objects in controller method.
in my case. my Controller methods takes this parameter.
"#AuthenticationPrincipal UserSession userSession"
when i exlude UserSession object swagger back to normal.
There were 2 way to do that
first is "#ApiIgnore #AuthenticationPrincipal UserSession userSession"
second is in swaggerConfig class
private Class[] clazz = {UserSession.class};
Docket().ignoredParameterTypes(clazz)
Incase someone needs a solution, what I did was as a work around for now.
in my service's code (response is a String)
return JsonFormat.printer().print(myProtoObject);
in my client's code:
Builder b = ProtoObject.newBuilder();
JsonFormat.parser().merge(result.getBody(), b);
ProtoObject protoObject = b.build();
As per their documentation from link https://docs.helloworks.com/v3/reference#callbacks
"With the HelloWorks API you can use callbacks to be notified about certain events. Currently we support a callback to be registered when a step is started, the cancellation of a workflow, and the completion of a workflow.
The callback URL will be called according to the following:
curl -X POST https://your.domain.com/some/path
-H "X-HelloWorks-Signature: {signature}"
-H "Content-Type: application/json"
-d "{payload}
I am not able to figure out how can I handle the callback in ASP.NET MVC 4.0. The callback returns data on JSON format. Once I receive the data, I can format it as per my need and can save to database. But how can I get the data in my controller? Guidance from experts on APIs are highly appreciated. Thanks in advance.
I am not able to figure out how can I handle the callback in ASP.NET MVC 4.0.
You need to have an api controller that accepts POST requests. That api endpoint is then called by the HelloWorks api. The fancy word to describe this mechanism is a Webhook. A nice introduction can be found here.
The very basic would be a controller like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MyWebAPI.Controllers
{
public class WebHookController : ApiController
{
// POST: api/webhook
public void Post([FromBody]string value)
{
}
}
}
You will need to register the url https://yourwebsite.domain/api/webhook at the HelloWorks api so it knows where to send the data to.
You probably want to secure this endpoint so others cannot abuse this api. See the docs for some guidance.
For example, in your case you should check that a header named "X-HelloWorks-Signature" is send in the request to the endpoint. The value of that header is a hash that should match the value of a hash of the content you received. To calculate the hash code to match create a hash using the SHA-256 algorithm and base16-encode the result.
There is also documentation from Microsoft on how to create web apis
Peter your guidance worked. I appreciate that. It was straight forward, only the technical jargon are making it intimidating :). Below are the code that worked. I am still to secure it using signature.
[HttpPost]
public ActionResult Callback()
{
string rawBody = GetDocumentContents(Request);
dynamic eventObj = JsonConvert.DeserializeObject(rawBody);
Test newTest = new Test();
newTest.Response = "Bikram-1" + (string)eventObj.type;
var test = db.Tests.Add(newTest);
db.SaveChanges();
return Content("Success!");
}
private string GetDocumentContents(HttpRequestBase Request)
{
string documentContents;
using (Stream receiveStream = Request.InputStream)
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
documentContents = readStream.ReadToEnd();
}
}
return documentContents;
}
I've created an OAuth2 authorization server using DotNetOpenAuth, which is working fine - I'm using the resource owner password flow, and successfully exchanging user credentials for an access token.
I now want to use that access token to retrieve data from secure endpoints in a ServiceStack API, and I can't work out how to do so. I've examined the Facebook, Google, etc. providers included with ServiceStack but it's not clear whether I should be following the same pattern or not.
What I'm trying to achieve (I think!) is
OAuth client (my app) asks resource owner ('Catherine Smith') for credentials
Client submits request to authorization server, receives an access token
Client requests a secure resource from the resource server (GET /users/csmith/photos)
The access token is included in an HTTP header, e.g. Authorization: Bearer 1234abcd...
The resource server decrypts the access token to verify the identity of the resource owner
The resource server checks that the resource owner has access to the requested resource
The resource server returns the resource to the client
Steps 1 and 2 are working, but I can't work out how to integrate the DotNetOpenAuth resource server code with the ServiceStack authorization framework.
Is there an example somewhere of how I would achieve this? I've found a similar StackOverflow post at How to build secured api using ServiceStack as resource server with OAuth2.0? but it isn't a complete solution and doesn't seem to use the ServiceStack authorization provider model.
EDIT: A little more detail. There's two different web apps in play here. One is the authentication/authorisation server - this doesn't host any customer data (i.e. no data API), but exposes the /oauth/token method that will accept a username/password and return an OAuth2 access token and refresh token, and also provides token-refresh capability. This is built on ASP.NET MVC because it's almost identical to the AuthorizationServer sample included with DotNetOpenAuth. This might be replaced later, but for now it's ASP.NET MVC.
For the actual data API, I'm using ServiceStack because I find it much better than WebAPI or MVC for exposing ReSTful data services.
So in the following example:
the Client is a desktop application running on a user's local machine, the Auth server is ASP.NET MVC + DotNetOpenAuth, and the Resource server is ServiceStack
The particular snippet of DotNetOpenAuth code that's required is:
// scopes is the specific OAuth2 scope associated with the current API call.
var scopes = new string[] { "some_scope", "some_other_scope" }
var analyzer = new StandardAccessTokenAnalyzer(authServerPublicKey, resourceServerPrivateKey);
var resourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(analyzer);
var wrappedRequest = System.Web.HttpRequestWrapper(HttpContext.Current.Request);
var principal = resourceServer.GetPrincipal(wrappedRequest, scopes);
if (principal != null) {
// We've verified that the OAuth2 access token grants this principal
// access to the requested scope.
}
So, assuming I'm on the right track, what I need to do is to run that code somewhere in the ServiceStack request pipeline, to verify that the Authorization header in the API request represents a valid principal who has granted access to the requested scope.
I'm starting to think the most logical place to implement this is in a custom attribute that I use to decorate my ServiceStack service implementations:
using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;
namespace SpotAuth.ResourceServer.Services {
[RequireScope("hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
This approach would also allow specifying the scope(s) required for each service method. However, that seems to run rather contrary to the 'pluggable' principle behind OAuth2, and to the extensibility hooks built in to ServiceStack's AuthProvider model.
In other words - I'm worried I'm banging in a nail with a shoe because I can't find a hammer...
OK, after a lot of stepping through the various libraries with a debugger, I think you do it like this: https://github.com/dylanbeattie/OAuthStack
There's two key integration points. First, a custom filter attribute that's used on the server to decorate the resource endpoints that should be secured with OAuth2 authorization:
/// <summary>Restrict this service to clients with a valid OAuth2 access
/// token granting access to the specified scopes.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
public class RequireOAuth2ScopeAttribute : RequestFilterAttribute {
private readonly string[] oauth2Scopes;
public RequireOAuth2ScopeAttribute(params string[] oauth2Scopes) {
this.oauth2Scopes = oauth2Scopes;
}
public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) {
try {
var authServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("authServer");
var dataServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("dataServer");
var tokenAnalyzer = new StandardAccessTokenAnalyzer(authServerKeys.PublicSigningKey, dataServerKeys.PrivateEncryptionKey);
var oauth2ResourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(tokenAnalyzer);
var wrappedRequest = new HttpRequestWrapper((HttpRequest)request.OriginalRequest);
HttpContext.Current.User = oauth2ResourceServer.GetPrincipal(wrappedRequest, oauth2Scopes);
} catch (ProtocolFaultResponseException x) {
// see the GitHub project for detailed error-handling code
throw;
}
}
}
Second, this is how you hook into the ServiceStack HTTP client pipeline and use DotNetOpenAuth to add the OAuth2 Authorization: Bearer {key} token to the outgoing request:
// Create the ServiceStack API client and the request DTO
var apiClient = new JsonServiceClient("http://api.mysite.com/");
var apiRequestDto = new Shortlists { Name = "dylan" };
// Wire up the ServiceStack client filter so that DotNetOpenAuth can
// add the authorization header before the request is sent
// to the API server
apiClient.LocalHttpWebRequestFilter = request => {
// This is the magic line that makes all the client-side magic work :)
ClientBase.AuthorizeRequest(request, accessTokenTextBox.Text);
}
// Send the API request and dump the response to our output TextBox
var helloResponseDto = apiClient.Get(apiRequestDto);
Console.WriteLine(helloResponseDto.Result);
Authorized requests will succeed; requests with a missing token, expired token or insufficient scope will raise a WebServiceException
This is still very much proof-of-concept stuff, but seems to work pretty well. I'd welcome feedback from anyone who knows ServiceStack or DotNetOpenAuth better than I do.
Update
On further reflection, your initial thought, to create a RequiredScope attribute would be a cleaner way to go. Adding it to the ServiceStack pipeline is as easy as adding the IHasRequestFilter interface, implementing a custom request filter, as documented here: https://github.com/ServiceStack/ServiceStack/wiki/Filter-attributes
public class RequireScopeAttribute : Attribute, IHasRequestFilter {
public void RequireScope(IHttpRequest req, IHttpResponse res, object requestDto)
{
//This code is executed before the service
//Close the request if user lacks required scope
}
...
}
Then decorate your DTO's or Services as you've outlined:
using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;
namespace SpotAuth.ResourceServer.Services {
[RequireScope("hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
}
Your RequireScope custom filter would be almost identical to ServiceStack's RequiredRoleAttribute implementation., so use it as a starting point to code from.
Alternately, you could map scope to permission. Then decorate your DTO or service accordingly (see SS wiki for details) for example:
[Authenticate]
[RequiredPermission("Hello")]
public class HelloService : Service {
public object Any(Hello request) {
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Normally ServiceStack calls the method bool HasPermission(string permission) in IAuthSession. This method checks if the list List Permissions in IAuthSession contains the required permission, so, in a custom IAuthSession you could override HasPermission and put your OAuth2 scopes checking there.
This is my unit test for create function :
public function testCreate() {
$this->routeMatch->setMatchedRouteName('restful');
$this->request->setMethod('POST')
->setContent('name=A');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(403, $response->getStatusCode());
$this->assertArrayHasKey('id', $result);
}
And this is my function :
public function create($data) {
if (empty($data)) {
$this->response->setStatusCode(400);
return;
}
for ($i = 0; $i < count(self::$ideas); $i++) {
if (self::$ideas[$i]['name'] == $data['name']) {
$this->response->setStatusCode(404);
return;
}
}
//#todo: secure the API
self::$index++;
$tmpArray = array('id'=>self::$index, 'name'=>$data['name']);
$this->response->setStatusCode(403);
}
But it seems that the $data is always blank. Am I wrong at the part writing unit test ?
When I try to use curl POST with -d, the $data has value as what I post through curl. I'm a quite confused what is wrong here ?
Thanks for reading and looking forward to your answer :)
Answer
I've came up with my successful unit test http://pastebin.com/fwFe0Mi3
For more information, I use this module to implement restful controller
If you take a look at \Zend\Mvc\Controller\AbstractRestfulController method processPostData you will notice that the method create in your controller is given an array of the post params from the request object.
If you look at \Zend\Http\Request the $postParams property is populated by the setPost method.
Now the child class \Zend\Http\PhpEnvironment\Request (used by ZF2 when you are requesting something) that extends \Zend\Http\Request (above) on instantiation (__contruct method) calls the setPost method (above) giving it the $_POST array.
This means that eventually ZF2 internally feeds your controller's create method with the $_POST contents and not by parsing the request body.
Now to your code.
I don't think dispatch will do anything without you having set up the event framework first. Instead you can call the controllers execute method providing it with an MvcEvent. The MvcEvent needs to have the request you instantiated set.
Secondly, as described above you need to call the request's setPost and give it an array for the create method to work properly. (On the other hand PUT reads the data from the request body)
Try doing that and if you are still having trouble I will try and give you an example soon.