Need help testing a REST controller that requires oauth2 - spring-boot

I am following this example for how to test my REST controller with oauth2. Testing an OAuth Secured API with Spring MVC
The code that I am stuck on is this line .with(httpBasic("fooClientIdPassword","secret"))
Does anyone know where is httpBasic method coming from? How is it instantiated, etc.? Thank you.
private String obtainAccessToken(String username, String password) throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "password");
params.add("client_id", "fooClientIdPassword");
params.add("username", username);
params.add("password", password);
ResultActions result
= mockMvc.perform(post("/oauth/token")
.params(params)
.with(httpBasic("fooClientIdPassword","secret"))
.accept("application/json;charset=UTF-8"))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"));
String resultString = result.andReturn().getResponse().getContentAsString();
JacksonJsonParser jsonParser = new JacksonJsonParser();
return jsonParser.parseMap(resultString).get("access_token").toString();
}

The httpBasic method comes from SecurityMockMvcRequestPostProcessors
I suppose you cannot find it cause you have not imported the dependency in your project. Once you add
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
in your pom you will be able to import and use it.

Related

How to authenticate Spring Boot rest API having POST method using Azure AD

In my spring boot rest api I am using a POST method. I am using Azure AD to authenticate api. When hitting an endpoint it is giving status as 200 OK but not doing the required POST operations. Even loggers are not getting printed from the controller #PostMapping
Can some help what needs to be fixed ...
In POM spring security and below dependency.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-spring-boot-starter</artifactId>
</dependency>
Registered required properties in properties file.
azure.activedirectory.session-stateless
azure.activedirectory.tenant-id
azure.activedirectory.user-group.allowed-groups
spring.security.oauth2.client.registration.azure.client-id
spring.security.oauth2.client.registration.azure.client-secret
NOTE: There is no front end as of now.
If you use #PostMapping to authenticate for access token, you don't need to use azure-spring-boot-starter. You could refer the code sample based on auth code flow:
Controller:
#PostMapping("/access_token")
public AuthenticationResult authorizeToken(#RequestBody #Valid AuthorizationRequest authorizationCode) throws Exception {
return tokenService.getAccessTokenFromAuthorizationCode(authorizationCode.getCode(), authorizationCode.getRedirectUri());
}
Service:
public AuthenticationResult getAccessTokenFromAuthorizationCode(String authorizationCode, String redirectUri) throws Exception {
AuthorizationCode request = new AuthorizationCode(authorizationCode);
try {
return tokenGenerator.getAccessToken(request, redirectUri);
} catch (Throwable throwable) {
return throwException(throwable);
}
}
TokenGenerator function:
public AuthenticationResult getAccessToken(
AuthorizationCode authorizationCode, String currentUri)
throws Throwable {
String authCode = authorizationCode.getValue();
ClientCredential credential = new ClientCredential(clientId,
clientSecret);
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(authority + tenant + "/", true,
service);
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, resource, null);
result = future.get();
} catch (ExecutionException e) {
throw e.getCause();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException(
"authentication result was null");
}
return result;
}
pom.xml
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>adal4j</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
application.properties
security.oauth2.client.clientId=xxx
security.oauth2.client.clientSecret=xxx
security.oauth2.client.tenant=xxx
security.oauth2.client.accessTokenUri=https://login.microsoftonline.com/<tenant-id>/oauth2/token
security.oauth2.client.userAuthorizationUri=https://login.microsoftonline.com/<tenant-id>/oauth2/authorize
security.oauth2.client.authority=https://login.microsoftonline.com/
security.oauth2.client.resource=https://graph.windows.net/ // scope of API
security.oauth2.resource.userInfoUri=https://graph.windows.net/me?api-version=1.6 // call API
If you would like to authenticate in backend with Spring Boot Starter, please refer to this example based on implicit grant flow.

How to work with Patch method in Webclient?

I have been searching a example for patch method in Webclient. I have a four fields in my Model class, I need to update one field (i.e status field), Hence I have decided to use Patch method. But I don't any examples in internet.
I have a piece of code in RestTemplate, here I need it in Webclient, as I'm migrating to Webclient. How to achieve below code?
public void updateProfile(UpdateProfile profile, String uniqueId) {
HttpHeaders headers = new HttpHeaders();
MediaType mediaType = new MediaType("application", "merge-patch+json");
headers.setContentType(mediaType);
HttpEntity<UpdateProfile> entity = new HttpEntity<>(profile, headers);
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.exchange(firebaseUrl+"/"+path+"/" + uniqueId + ".json",
HttpMethod.PATCH, entity, Void.class);
}
You can use the following patch method sample from the web client, i have not tested but i hope so you can use the similar approach.
WebClient webClient = WebClient.create(firebaseUrl);
webClient.patch()
.uri("+path+" + uniqueId + ".json")
.contentType(MediaType.valueOf("application/json-patch+json"))
.bodyValue(data)
.exchange();
remember to add the below artifact in your pom.xml,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

CamelHttp multipart/form-data upload. Changes between camel-http4 and camel-http-starter

I have a Spring Boot app that uses Camel to POST a multipart/form-data request to a REST endpoint. The request includes a text part and a file part. The code that builds the request is as follows:
#Override
public void process(Exchange exchange) throws Exception {
#SuppressWarnings("unchecked")
Map<String, String> body = exchange.getIn().getBody(Map.class);
String fileName = body.get("FILE_NAME");
String filePath = body.get("FILE_PATH");
MultipartEntityBuilder entity = MultipartEntityBuilder.create();
entity.addTextBody("name", fileName, ContentType.DEFAULT_TEXT);
entity.addBinaryBody("file", new File(filePath),
ContentType.APPLICATION_OCTET_STREAM, fileName);
exchange.getIn().setBody(entity.build());
}
.to("https4://<endpoint>")
This code works nicely.
In my pom.xml file I am importing the camel-http4 component:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http4</artifactId>
<version>${camel.version}</version>
</dependency>
I have tried replacing the camel-http4 component with camel-http-starter, as suggested by the latest Camel documentation at https://camel.apache.org/components/latest/http-component.html
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http-starter</artifactId>
<version>${camel.version}</version>
</dependency>
and replace all http4 endpoints with http, but the code above now fails because there is no type converter available between HttpEntity and InputStream in the camel-http component.
I have tried using Camel's MIME Multipart DataFormat:
.setHeader("name", simple("${body[FILE_NAME]}"))
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
#SuppressWarnings("unchecked")
Map<String, String> body = exchange.getIn().getBody(Map.class);
String filePath = body.get("FILE_PATH");
exchange.getIn().setBody(new File(filePath));
}
})
// Add only "name" header in multipart request
.marshal().mimeMultipart("form-data", true, true, "(name)", true)
.to("https://<endpoint>")
But I keep getting HTTP 400 errors from the server, meaning it doesn't understand the request.
So my question is:
How to use the MIME Multipart DataFormat (or any other way) to obtain the same multipart request as the previous working code?

How can I add my business logic to the authentication of spring cloud oauth?

In the oauth server side(auth microservice),I try to implement the oauth center by spring security and jwt, here is the Code of my UserDetailService:
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
ManagerInfo managerInfo = userMapper.getUserByLoginName(username);
if(managerInfo ==null)
throw new UsernameNotFoundException("sorry,can not find user of "+username+"!");
Collection<Role> roleList =roleMapper.getRoleByUserId(managerInfo.getId());
String roleIds = roleList.stream().map(role ->role.getId().toString()).collect(Collectors.joining(","));
Collection<Authority> authorities =authorityMapper.getAuthoritiesByRoleIds(roleIds);
CustomUserPrincipal userDetail = new CustomUserPrincipal(managerInfo.getUsername(),managerInfo.getPassword(),
authorities);
userDetail.setUser(managerInfo);
return userDetail;
}
The CustomUserprincipal just extends org.springframework.security.core.userdetails.User,and the Authority implements org.springframework.security.core.GrantedAuthority.
In my gateway project , I try to use spring boot(version:1.5.9.RELEASE) and here is some dependency:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
Now,I have successfully authenticate the user by "prePostEnabled" way,here is the controller code:
#RequestMapping(value = "/getUserById/{id}",method = RequestMethod.GET)
#PreAuthorize("hasAnyAuthority('LIST_USER')")
CollectionAccount getUserById(#PathVariable String id){
return managerService.getCollectionAccount(Long.parseLong(id));
}
All the code I have written works great,and the user have 'LIST USER' authority which is returned by the userdetailservice can access this method.
But,what if I want to authenticate the user by resource type,not just the authority string?
Or add some business logic to the authentication, like 'A_PARK_LIST_USER','B_PARK_LIST_USER' and so on,maybe the user login in just have 'A_PARK_LIST_USER' and can not list the user of "B_PARK",so I can not control the access right just by'LIST USER', how can I do that?
You just need to inject the AuthenticationPrincipal into the controller method.
Try this:
#RequestMapping(value = "/getUserById/{id}",method = RequestMethod.GET)
#PreAuthorize("hasAnyAuthority('LIST_USER')")
CollectionAccount getUserById(#PathVariable String id, #AuthenticationPrincipal Principal principal){
//Here you have the users information, the principal.getName() will be the username that has been authenticated
log.debug(principal.getName());
return managerService.getCollectionAccount(Long.parseLong(id));
}

spring boot with restcontroller using jsonobject not working

I am using spring boot, and while making a restcontroller or controller if I use the jsonobject type request then it doesnt work, whereas same works when I change type to string.
#Controller
#RequestMapping("rest/dummy")
public class CustomerController {
#GetMapping("test")
public ResponseEntity test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return new ResponseEntity(inputData.toString(), HttpStatus.OK);
}
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
I do want to use it both GET and POST type and also I want to use jsonobject for both request and response as the data can change on fly and its type.
In RequestParam , we send key values which added in URL,
To send Json object send it in RequestBody .
Use #RequestBody and send your Json in body part of your request.
Using real POJOs as params and return values is the better approach imo. Use Jackson annotations to configure those POJOs.
Anyways. This should work:
#GetMapping("test")
public ResponseEntity<String> test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return ResponseEntity.ok(inputData.toString());
}
alternatively
#GetMapping("test")
public ResponseEntity<SomeOutputDto> test(#RequestParam("req") String inputData) {
SomeOutputDto out = new SomeOutputDto();
out.setAbc(123);
return ResponseEntity.ok(dto);
}
this requires a additional class: SomeOutputDto, but on the other hand, you have more control over your code.
public class SomeOutputDto {
private int abc = 0;
public void setAbc(int v) {
this.abc = v;
}
public int getAbc() { return this.abc; }
}
Got it working by using apache-tomcat 8.0.15, the same doesnt work with apache-tomcat 8.0.49

Resources