Spring cloud gateway test - integration-testing

I have implemented simple gateway with spring cloud gateway.
It redirects requests from {host-gateway}/gateway/{uri} to {routes.uri}/{uri}, if uri starts from service name from list routes.services
#Configuration
public class DefaultRoutesConfig {
#Value("#{'${routes.services}'.split(',')}")
private List<String> services;
#Value("${routes.uri}")
private String uri;
#Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routesBuilder = builder.routes();
for (String id : services) {
routesBuilder.route(id, p -> p
.path("/gateway/" + id +"/**")
.filters(f -> f.stripPrefix(1))
.uri(uri)
);
}
return routesBuilder.build();
}
}
How to write integration test for this logic?

Related

Controller constructor does not get called

Hello i am trying to understand why do my requests not enter my api route.They seem to reach the server but they wont fan out in the MVC.
The server is running on: http://localhost:9300
The route i am requesting is : http://localhost:9300/api/getusers
Program
public class Program {
public static void Main(string[] args) {
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) {
var builder = new WebHostBuilder();
builder.UseStartup<Startup>();
var url = Address.Default.ToUrl();
builder.UseKestrel().UseUrls(url);
return builder;
}
}
Startup
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services.AddOptions();
services.AddMvc();
}
public IConfiguration Configuration;
public void Configure(IApplicationBuilder app) {
Debug.WriteLine("Entered server"); //enters successfully here
app.UseMvc(); //does not enter the controller
}
}
Controller
This is a simple controller with a GET method.The constructor is not invoked at all.Why would this happen?I know it when the server runs the first time ..it does a health check on its routes.
[ApiController]
class UserController : ControllerBase {
private static List<User> users = new List<User> {
new User{Id=0,Age=0,Name="Failed"},
new User{Id=12,Age=33,Name="Daniel"},
new User{Id=13,Age=33,Name="Marian"},
};
public UserController() {
Debug.WriteLine("Controller called"); //does not get called !
}
[HttpGet]
[Route("api/getusers")]
public async Task<HttpResponseMessage> GetUsers() {
await Task.Delay(1000);
return new HttpResponseMessage {
Content = new StringContent(users.ToJson()),
StatusCode = HttpStatusCode.OK
};
}
}
P.S Do i have to add anyything ? What am i missing i followed other implementations closely.
I've created the webapi project using dotnet new webapi.
I've managed to get to the url with the similar configuration by changing the access modifier of a similar controller. Try to add public keyword to the class UserController. So it should be public class UserController
I will provide more information about the configuration of the project if it is necessary and the step above does not help.

Override UserAuthenticationConverter for JWT OAuth Tokens

I am trying to create a spring resource server secured with oauth2.
I am using auth0 for my auth2 service, and I have an api and client configured with scopes.
I have a resource server that mostly works. It is secured, and I can use #EnableGlobalMethodSecurity and #PreAuthorize("#oauth2.hasScope('profile:read')") to limit access to tokens with that scope.
However, when I try to get the Principal or the OAuth2Authentication they are both null. I've configured the resource server to use the JWK key-set-uri.
I suspect that this has to do with the DefaultUserAuthenticationConverter trying to read the the 'user_name' claim form the JWT, but it needs to be reading it from the 'sub' claim, and I don't know how to change this behaviour.
First create a UserAuthenticationConverter:
public class OidcUserAuthenticationConverter implements UserAuthenticationConverter {
final String SUB = "sub";
#Override
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication) {
throw new UnsupportedOperationException();
}
#Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(SUB)) {
Object principal = map.get(SUB);
Collection<? extends GrantedAuthority> authorities = null;
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}
}
Then configure spring to use it like so:
#Configuration
public class OidcJwkTokenStoreConfiguration {
private final ResourceServerProperties resource;
public OidcJwkTokenStoreConfiguration(ResourceServerProperties resource) {
this.resource = resource;
}
#Bean
public TokenStore jwkTokenStore() {
DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new OidcUserAuthenticationConverter());
return new JwkTokenStore(this.resource.getJwk().getKeySetUri(), tokenConverter);
}
}

RequestIntercepor for a specific FeignClient

I have a RequestInterceptor where I automatically copy an AccessToken from OAuth2ClientContext into the RequestTemplate's header so that the internal services are seamlessly calling one another with the same AccessToken that came from the mobile device that started the scenario.
And that's how we manage services methods authorization.
This is the interceptor code:
public class FeignOAuthInterceptor implements RequestInterceptor {
private OAuth2ClientContext oauth2ClientContext;
public FeignOAuthInterceptor (OAuth2ClientContext oauth2ClientContext) {
this.oauth2ClientContext = oauth2ClientContext;
}
#Override
public void apply(RequestTemplate template) {
if (!template.headers().containsKey(PropertyBagFilter.AUTHORIZATION_HEADER) && oauth2ClientContext.getAccessTokenRequest().getExistingToken() != null) {
template.header(PropertyBagFilter.AUTHORIZATION_HEADER, String.format("%s %s", PropertyBagFilter.BEARER_TOKEN_TYPE,
oauth2ClientContext.getAccessTokenRequest().getExistingToken().toString()));
}
}
}
and this is the #Beans configuration:
#Bean
public OAuth2ClientContext oAuth2ClientContext (){
return new DefaultOAuth2ClientContext();
}
#Bean
public RequestInterceptor feignOAuthInterceptor(OAuth2ClientContext oauth2ClientContext) {
return new FeignOAuthInterceptor(oauth2ClientContext);
}
#Bean
public OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails(){
return new ResourceOwnerPasswordResourceDetails();
}
The problem is that there are different FeignClients and part of them are for 3rd party services, such as a services which we use for SMS texts and I don't want to send the AccessToken there.
How can I determine inside the RequestInterceptor what FeignClient it came from?

Dependency Injection on API to API with AutoRest

I been following the Swagger in Azure App Service tutorial and I notice the AutoREST code generation. In the tutorial, theres is an API and a DataAPI.
The TodoListAPI is a normal Web API.
The TodoListDataAPI is the one that is connected to a datasource, it is also a Web API and it is being consumed by TodoListAPI.
Using swagger autogerated codes are being imported to the TodoListAPI
partial interface ITodoListDataAPI: IDisposable
{
Uri BaseUri
{
get; set;
}
ServiceClientCredentials Credentials
{
get; set;
}
IToDoList ToDoList
{
get;
}
....
/// this seems to be the interface that is needed to be injected in the Controller
public partial interface IToDoList
{
Task<HttpOperationResponse<object>> DeleteByOwnerAndIdWithOperationResponseAsync(string owner, int id, CancellationToken cancellationToken = default(System.Threading.CancellationToken));
Task<HttpOperationResponse<ToDoItem>> GetByIdByOwnerAndIdWithOperationResponseAsync(string owner, int id, CancellationToken cancellationToken = default(System.Threading.CancellationToken));
Then in the ToDoListAPI controller it is being used like this
public class ToDoListController : ApiController
{
private string owner = "*";
private static ITodoListDataAPINewDataAPIClient()
{
var client = new TodoListDataAPI(new Uri(ConfigurationManager.AppSettings["ToDoListDataAPIUrl"]));
return client;
}
// GET: api/ToDoItemList
public async Task<IEnumerable<ToDoItem>> Get()
{
using (var client = NewDataAPIClient())
{
var results = await client.ToDoList.GetByOwnerAsync(owner);
....
}
}
}
Now the problem in this pattern is it is not testable because it directly consumes the DataAPI.
My question is, How can I make ITodoList to be used as dependency injection on the controller.
public class ToDoListController : ApiController
{
private readonly ITodoListDataAPI _todoListData;
private ToDoListController (IToDoList todoListData)
{
_todoListData = todoListData;
}
}
I also don't know what Autofoca DI library to use, there is Autofac and Autofac.WebApi in the nuget gallery and I am not sure what to use in these instance.
Thanks,

how to support Polymorphism with RESTeasy Client proxy?

suppose this JAX-RS method :
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Employee get(#PathParam("id") Long id) {
return myService.findbyId(id);
}
With the following POJO:
#XmlRootElement
public class Employee {
Integer id;
String name; (getters;setters etc...)
}
#XmlRootElement(name="employee")
public class SepcialEmployee extends Employee {
Skill skill; (getters;setters etc...)
}
#XmlRootElement(name="employee")
public class Manager extends Employee {
String headOffice; (getters;setters etc...)
}
This works fine with RESTeasy/spring-MVC integration. And if I call the method from a web browser; I can get the following answer for i.e.:
<employee Id="17">
<name>Marc</name>
<headOffice>accounting</headOffice>
</employee>
But if I use the RESTeasy Client Framework for my unit test. the client proxy generated unmarsalles only the Employee Parent class and I loose the child informations (Manager.headOffice or SepcialEmployee.Skill). Below an extract of my Junit test:
public class Test {
#Path("empl")
public interface EmpProxy {
#GET
#Produces(MediaType.APPLICATION_XML)
Employee getEmployee(#ClientURI String uri);
}
private static TJWSEmbeddedSpringMVCServer server;
public static final String host = "http://localhost:8080/";
public static final int port = 8080;
private static EmpProxy proxy;
#BeforeClass
public static void setup() {
server = new TJWSEmbeddedSpringMVCServer("classpath:test-dispatcher-servlet.xml", port);
server.start();
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target(host);
proxy = target.proxy(EmpProxy.class);
}
#Test
public void test(){
String url = host+"/empl/17";
Employee employee = proxy.getEmployee(url);
System.out.println(employee);
}
}

Resources