Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish fix no permit all #69

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import cz.cvut.kbss.study.service.ExcelRecordConverter;
import cz.cvut.kbss.study.service.PatientRecordService;
import cz.cvut.kbss.study.service.UserService;
import cz.cvut.kbss.study.service.security.SecurityUtils;
import cz.cvut.kbss.study.util.ConfigParam;
import cz.cvut.kbss.study.util.Constants;
import jakarta.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -57,20 +58,22 @@ public class PatientRecordController extends BaseController {
private final ExcelRecordConverter excelRecordConverter;
private final RestTemplate restTemplate;
private final ConfigReader configReader;
private final SecurityUtils securityUtils;
private ObjectMapper objectMapper;
private final UserService userService;

public PatientRecordController(PatientRecordService recordService, ApplicationEventPublisher eventPublisher,
ExcelRecordConverter excelRecordConverter, RestTemplate restTemplate,
ConfigReader configReader, ObjectMapper objectMapper,
UserService userService) {
UserService userService, SecurityUtils securityUtils) {
this.recordService = recordService;
this.eventPublisher = eventPublisher;
this.excelRecordConverter = excelRecordConverter;
this.restTemplate = restTemplate;
this.configReader = configReader;
this.objectMapper = objectMapper;
this.userService = userService;
this.securityUtils = securityUtils;
}

@PreAuthorize("hasRole('" + SecurityConstants.ROLE_ADMIN + "') or #institutionKey==null or @securityUtils.isMemberOfInstitution(#institutionKey)")
Expand Down Expand Up @@ -233,11 +236,11 @@ public String getFilename() {
// Create HttpEntity
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
String authHeader = securityUtils.getPublishToken();
if (authHeader != null && !authHeader.isBlank()) {
headers.set(HttpHeaders.AUTHORIZATION, authHeader);
headers.setBearerAuth(authHeader);
} else {
throw new RuntimeException("Authorization header missing in request");
throw new SecurityException("Could not retrieve publish token.");
}
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,28 @@
import cz.cvut.kbss.study.security.model.Role;
import cz.cvut.kbss.study.security.model.UserDetails;
import cz.cvut.kbss.study.service.ConfigReader;
import cz.cvut.kbss.study.util.ConfigParam;
import cz.cvut.kbss.study.util.oidc.OidcGrantedAuthoritiesExtractor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.web.server.authentication.SwitchUserWebFilter;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
Expand All @@ -31,11 +41,13 @@ public class SecurityUtils {
private final PatientRecordDao patientRecordDao;

private final ConfigReader config;
private final RestTemplate restTemplate;

public SecurityUtils(UserDao userDao, PatientRecordDao patientRecordDao, ConfigReader config) {
public SecurityUtils(UserDao userDao, PatientRecordDao patientRecordDao, ConfigReader config, RestTemplate restTemplate) {
this.userDao = userDao;
this.patientRecordDao = patientRecordDao;
this.config = config;
this.restTemplate = restTemplate;
}

/**
Expand Down Expand Up @@ -128,4 +140,46 @@ public boolean areFromSameInstitution(String username) {
final List<User> users = userDao.findByInstitution(user.getInstitution());
return users.stream().anyMatch(o -> o.getUsername().equals(username));
}

public String getCurrentToken(){
// Retrieve token from security context
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// Check if token is Jwt
if(! (authentication.getPrincipal() instanceof Jwt))
throw new IllegalArgumentException("Cannot process request, authentication principal type \"%s\" is not supported.".formatted(authentication.getPrincipal().getClass()));

// This is only for Jwt type tokens
return ((Jwt)authentication.getPrincipal()).getTokenValue();
}

public String getPublishToken(){
// TODO - The exchanged token does not contain an IDP field, so the supplier won't know the original identity provider
String accessToken = getCurrentToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange");
map.add("subject_token", accessToken);
map.add("subject_token_type", "urn:ietf:params:oauth:token-type:jwt"); // specify that the
// map.add("subject_issuer", "vpaf-keycloak-oidc"); // TODO - check if needed
// map.add("subject_issuer", "https://kbss.felk.cvut.cz/vietnam-peoples-air-force/services/auth/realms/record-manager");
// map.add("requested_token_type", "urn:ietf:params:oauth:token-type:access_token"); // TODO - check if needed
// map.add("requested_issuer", "vpaf-keycloak-oidc"); // TODO - attempt to include idp field in the exchanged token, not working.
map.add("client_id", "record-manager-server"); // The client_id of the publishing service, i.e. the suppliers record-manager-server
map.add("client_secret", config.getConfig(ConfigParam.PUBLISH_RECORDS_SERVICE_SECRET)); // the client secret for the client_id
// map.add("audience", "record-manager-server"); // TODO - check if needed, without it the aud field in the token does not contain record-manager-server

String exchangedToken = null;
ResponseEntity<Map> response = null;
try {
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
response = restTemplate.postForEntity(config.getConfig(ConfigParam.EXCHANGE_TOKEN_SERVICE_URL), request, Map.class);
// response = restTemplate.postForEntity("https://kbss.felk.cvut.cz/ava/services/auth/realms/record-manager/protocol/openid-connect/token", request, Map.class);

return response.getBody().get("access_token").toString();

}catch(Exception e){
throw new SecurityException("Error exchanging token", e);
}
}
}
2 changes: 2 additions & 0 deletions src/main/java/cz/cvut/kbss/study/util/ConfigParam.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public enum ConfigParam {

ON_UPDATE_RECORD_SERVICE_URL("onRecordUpdateServiceUrl"),
ON_PUBLISH_RECORDS_SERVICE_URL("onPublishRecordsServiceUrl"),
PUBLISH_RECORDS_SERVICE_SECRET("publishRecordsServiceSecret"),
EXCHANGE_TOKEN_SERVICE_URL("exchangeTokenServiceUrl"),
EXCEL_IMPORT_SERVICE_URL("excelImportServiceUrl"),

APP_CONTEXT("appContext"),
Expand Down
Loading