Skip to content

Commit

Permalink
IM-459 Use Klab_Authorization header instead Authorization and add se…
Browse files Browse the repository at this point in the history
…ssion in EngineSessionController

EngineSessionController uses PreauthenticationFilter to set the session in the principal.
Now the principal is JWT; the session is retrieved from request headers.
  • Loading branch information
kristinaBc3 committed Nov 20, 2024
1 parent b1e1386 commit 9f69087
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.integratedmodelling.klab.api.auth;

/**
* Defines constants for common HTTP headers.
*
* <p>This interface provides a set of predefined header names used
* in HTTP requests and responses. These constants can be used to
* avoid hardcoding string values and reduce errors.</p>
*
* @author Kristina
*/

public interface KlabHttpHeaders {

/**
* Designed to send session information with requests.
**/
public static final String KLAB_AUTHORIZATION = "Klab_Authorization";

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
*/
@RestController
@CrossOrigin(origins = "*")
@Secured(Roles.PUBLIC)
//@Secured(Roles.PUBLIC)
public class KlabController {

@RequestMapping(value = API.ENGINE.RESOURCE.GET_PROJECT_RESOURCE, method = RequestMethod.GET)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ public class EnginePublicController implements API.PUBLIC {
@RequestMapping(value = CREATE_CONTEXT, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public TicketResponse.Ticket contextRequest(@RequestBody ContextRequest request,
@RequestHeader(name = "Authorization") String session) {
@RequestHeader(name = "Klab_Authorization") String klabAuth, @RequestHeader(name = "Authorization") String auth) {

String session = klabAuth == null ? klabAuth : auth;

Session s = Authentication.INSTANCE.getIdentity(session, Session.class);
if (s == null) {
Expand Down Expand Up @@ -109,7 +111,7 @@ public TicketResponse.Ticket contextRequest(@RequestBody ContextRequest request,
@RequestMapping(value = OBSERVE_IN_CONTEXT, method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public TicketResponse.Ticket observationRequest(@RequestBody ObservationRequest request,
@RequestHeader(name = "Authorization") String session, @PathVariable String context) {
@RequestHeader(name = "Klab_Authorization") String session, @PathVariable String context) {

Session s = Authentication.INSTANCE.getIdentity(session, Session.class);

Expand Down Expand Up @@ -143,7 +145,7 @@ public TicketResponse.Ticket observationRequest(@RequestBody ObservationRequest

@RequestMapping(value = SUBMIT_ESTIMATE, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public TicketResponse.Ticket submitEstimate(@RequestHeader(name = "Authorization") String session,
public TicketResponse.Ticket submitEstimate(@RequestHeader(name = "Klab_Authorization") String session,
@PathVariable String estimate) {

Session s = Authentication.INSTANCE.getIdentity(session, Session.class);
Expand All @@ -163,7 +165,8 @@ public TicketResponse.Ticket submitEstimate(@RequestHeader(name = "Authorization
}

if (est.contextRequest != null) {
return contextRequest(est.contextRequest, session);
//TODO only 1 sessioin parameter
return contextRequest(est.contextRequest, session, session);
}

return observationRequest(est.observationRequest, session, est.observationRequest.getContextId());
Expand All @@ -173,7 +176,7 @@ public TicketResponse.Ticket submitEstimate(@RequestHeader(name = "Authorization
MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_PDF_VALUE, MediaType.IMAGE_PNG_VALUE, "text/csv", "image/tiff",
"application/vnd.ms-excel", "application/octet-stream",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"})
public void exportData(@PathVariable String export, @RequestHeader(name = "Authorization") String session,
public void exportData(@PathVariable String export, @RequestHeader(name = "Klab_Authorization") String session,
@PathVariable String observation, @RequestHeader(name = "Accept") String format,
@RequestParam(required = false) String view, @RequestParam(required = false) String viewport,
@RequestParam(required = false) String locator, HttpServletResponse response) throws IOException {
Expand Down Expand Up @@ -385,7 +388,7 @@ private void outputImage(IObservation obs, HttpServletResponse response, Export

@RequestMapping(value = TICKET_INFO, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public TicketResponse.Ticket getTicketInfo(@RequestHeader(name = "Authorization") String session,
public TicketResponse.Ticket getTicketInfo(@RequestHeader(name = "Klab_Authorization") String session,
@PathVariable String ticket) {

Session s = Authentication.INSTANCE.getIdentity(session, Session.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
package org.integratedmodelling.klab.engine.rest.controllers.engine;

import java.security.Principal;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.integratedmodelling.klab.Authentication;
import org.integratedmodelling.klab.api.API;
import org.integratedmodelling.klab.api.auth.IIdentity;
import org.integratedmodelling.klab.api.auth.KlabHttpHeaders;
import org.integratedmodelling.klab.api.auth.Roles;
import org.integratedmodelling.klab.api.runtime.ISession;
import org.integratedmodelling.klab.engine.runtime.Session;
import org.integratedmodelling.klab.exceptions.KlabInternalErrorException;
import org.integratedmodelling.klab.rest.SessionReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
* The controller implementing the {@link org.integratedmodelling.klab.api.API.ENGINE.SESSION session API}.
* The controller implementing the
* {@link org.integratedmodelling.klab.api.API.ENGINE.SESSION session API}.
*
* @author ferdinando.villa
*
Expand All @@ -26,20 +42,57 @@
@Secured(Roles.SESSION)
public class EngineSessionController {

private static final Logger logger = LoggerFactory.getLogger(EngineSessionController.class);

@RequestMapping(value = API.ENGINE.SESSION.INFO, method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public SessionReference describeObservation(Principal principal) {
public SessionReference describeObservation(Principal principal, HttpServletRequest httpRequest) {

Map<String, String> headers = Collections.list(httpRequest.getHeaderNames()).stream()
.collect(Collectors.toMap(h -> h, httpRequest::getHeader));
ISession session = getSession(principal);
return ((Session)session).getSessionReference();
return ((Session) session).getSessionReference();
}

public static ISession getSession(Principal principal) {
if (principal instanceof PreAuthenticatedAuthenticationToken
|| !(((PreAuthenticatedAuthenticationToken) principal).getPrincipal() instanceof ISession)) {

if (principal instanceof PreAuthenticatedAuthenticationToken) {
return (ISession) ((PreAuthenticatedAuthenticationToken) principal).getPrincipal();
}

if (getCurrentHttpRequest() != null) {
String klabAuth = getCurrentHttpRequest().getHeader(KlabHttpHeaders.KLAB_AUTHORIZATION);

if (klabAuth != null) {
// send anything already known downstream
if (Authentication.INSTANCE.getIdentity(klabAuth, IIdentity.class) != null) {

IIdentity identity = Authentication.INSTANCE.getIdentity(klabAuth, IIdentity.class);
// known k.LAB identities are UserDetails and have established roles
if (identity instanceof UserDetails) {
return (ISession) identity;
} else if (identity != null) {
throw new KlabInternalErrorException(
"internal error: non-conformant session identity in Authentication catalog! "
+ identity);
}
}
}
}

throw new IllegalStateException(
"request was not authenticated using a session token or did not use preauthentication");

}

private static HttpServletRequest getCurrentHttpRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
return request;
}
logger.debug("Not called in the context of an HTTP request");
return null;
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package org.integratedmodelling.klab.engine.rest.security;

import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.integratedmodelling.klab.Authentication;
import org.integratedmodelling.klab.api.auth.IIdentity;
import org.integratedmodelling.klab.api.auth.KlabHttpHeaders;
import org.integratedmodelling.klab.utils.IPUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
Expand All @@ -12,12 +17,27 @@ public class PreauthenticationFilter extends AbstractPreAuthenticatedProcessingF

@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
HttpServletRequest httpRequest = (HttpServletRequest) request;

Map<String, String> headers = Collections.list(httpRequest.getHeaderNames())
.stream()
.collect(Collectors.toMap(h -> h, httpRequest::getHeader));
String auth = request.getHeader(HttpHeaders.AUTHORIZATION);
if (auth != null) {
String klabAuth = request.getHeader(KlabHttpHeaders.KLAB_AUTHORIZATION);

if (auth != null ) {
// send anything already known downstream
if (Authentication.INSTANCE.getIdentity(auth, IIdentity.class) != null) {
return auth;
}

}

if (klabAuth != null ) {
// send anything already known downstream
if (Authentication.INSTANCE.getIdentity(klabAuth, IIdentity.class) != null) {
return klabAuth;
}
return null;
}
if (IPUtils.isLocal(request.getRemoteAddr())) {
Expand All @@ -28,9 +48,22 @@ protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {

@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
Map<String, String> headers = Collections.list(request.getHeaderNames())
.stream()
.collect(Collectors.toMap(h -> h, request::getHeader));
String auth = request.getHeader(HttpHeaders.AUTHORIZATION);
String klabAuth = request.getHeader(KlabHttpHeaders.KLAB_AUTHORIZATION);
// returning null will refuse authentication
return auth == null ? "dummycredentials" : auth;
if (auth == null && klabAuth == null) {
return "dummycredentials";
} else if (auth != null && klabAuth != null) {
return klabAuth;
} else if (auth == null && klabAuth != null) {
return klabAuth;
} else {
return auth;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,38 +53,36 @@ class WebSecurityConfigRemote extends WebSecurityConfigurerAdapter{
@Autowired
private PreauthenticatedUserDetailsService customUserDetailsService;

@Autowired
private EngineDirectoryAuthenticationProvider authProvider;

@Bean
AuthoritiesConverter realmRolesAuthoritiesConverter() {
return claims -> {
final var realmAccess = Optional.ofNullable((Map<String, Object>) claims.get("realm_access"));
final var roles =
realmAccess.flatMap(map -> Optional.ofNullable((List<String>) map.get("roles")));

roles.ifPresent(role -> role.add(Roles.PUBLIC));

List<GrantedAuthority> rolesList = roles.map(List::stream).orElse(Stream.empty()).map(SimpleGrantedAuthority::new)
.map(GrantedAuthority.class::cast).toList();

return rolesList;
};
}

@Bean
JwtAuthenticationConverter authenticationConverter(
Converter<Map<String, Object>, Collection<GrantedAuthority>> authoritiesConverter) {
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter
.setJwtGrantedAuthoritiesConverter(jwt -> authoritiesConverter.convert(jwt.getClaims()));
return jwtAuthenticationConverter;
}

// @Bean
// AuthoritiesConverter realmRolesAuthoritiesConverter() {
// return claims -> {
// final var realmAccess = Optional.ofNullable((Map<String, Object>) claims.get("realm_access"));
// final var roles =
// realmAccess.flatMap(map -> Optional.ofNullable((List<String>) map.get("roles")));
//
// roles.ifPresent(role -> role.add(Roles.PUBLIC));
//
// List<GrantedAuthority> rolesList = roles.map(List::stream).orElse(Stream.empty()).map(SimpleGrantedAuthority::new)
// .map(GrantedAuthority.class::cast).toList();
//
// return rolesList;
// };
// }
//
// @Bean
// JwtAuthenticationConverter authenticationConverter(
// Converter<Map<String, Object>, Collection<GrantedAuthority>> authoritiesConverter) {
// JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
// jwtAuthenticationConverter
// .setJwtGrantedAuthoritiesConverter(jwt -> authoritiesConverter.convert(jwt.getClaims()));
// return jwtAuthenticationConverter;
// }

@Override
protected void configure(HttpSecurity http) throws Exception {

http.cors().and().csrf().disable().addFilterBefore(certFilter(), RequestHeaderAuthenticationFilter.class)
http.cors().and().csrf().disable().addFilterAfter(certFilter(), RequestHeaderAuthenticationFilter.class)
.authorizeHttpRequests(
authorize -> authorize.mvcMatchers("/api/**").authenticated().mvcMatchers("/**").permitAll())
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
Expand All @@ -96,16 +94,16 @@ protected void configure(HttpSecurity http) throws Exception {
protected AuthenticationManager authenticationManager() {
final List<AuthenticationProvider> providers = new ArrayList<>(2);
providers.add(preauthAuthProvider());
providers.add(authProvider);
return new ProviderManager(providers);
}

@Bean(name="certFilter")
@Bean(name="certFilter")
PreauthenticationFilter certFilter() {
PreauthenticationFilter ret = new PreauthenticationFilter();
ret.setAuthenticationManager(authenticationManager());
return ret;
}


@Bean(name = "preAuthProvider")
PreAuthenticatedAuthenticationProvider preauthAuthProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ public class HubUserService implements RemoteUserService {
@Override
public ResponseEntity< ? > login(RemoteUserAuthenticationRequest login) {
ResponseEntity<HubLoginResponse> result = null;
//if (!"".equals(login.getUsername()) && !"".equals(login.getPassword())) {
if (activeProfile.equals("engine.remote")) {
if (!"".equals(login.getUsername()) && null==login.getToken()) {
// if (activeProfile.equals("engine.remote")) {
login.setRemote(true);
try {
result = hubLogin(login);
Expand Down Expand Up @@ -271,9 +271,16 @@ private Session activeSession(HubUserProfile profile, String token) {
}

private ResponseEntity<HubLoginResponse> hubLogin(UserAuthenticationRequest login) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization"));
HttpEntity< ? > request = new HttpEntity<>(login, headers);
HttpEntity< ? > request = new HttpEntity<>(login);

String authorization = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization");

if (authorization != null) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization"));
request.getHeaders().addAll(headers);
}

return restTemplate.postForEntity(getLoginUrl(), request, HubLoginResponse.class);
}

Expand All @@ -287,8 +294,7 @@ private URI hubLogout(String token) {

private ResponseEntity<HubUserProfile> hubToken(String token) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authentication", token);
headers.add("Authorization", ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization"));
headers.add("Authentication", token);
HttpEntity< ? > request = new HttpEntity<>(headers);
ResponseEntity<HubUserProfile> response = restTemplate.exchange(getProfileUrl(), HttpMethod.GET, request,
HubUserProfile.class, true);
Expand Down

0 comments on commit 9f69087

Please sign in to comment.