Skip to content
Open
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
@@ -0,0 +1,38 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.api.registration.cern;

import java.util.Optional;

import org.springframework.context.annotation.Profile;
import org.springframework.web.client.RestClientException;

import it.infn.mw.iam.api.registration.cern.dto.VOPersonDTO;

@Profile("cern")
public interface CernSecurityBlockingApiService {

/**
* Returns an @Optional object that contains the @VOPersonDTO related to the CERN personId
* provided as parameter or empty if not found.
*
* @param personId
* @return
* @throws RestClientException in case of ApiErrors
*/
Optional<VOPersonDTO> getSecurityBlockingRecord(String personId) throws RestClientException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.api.registration.cern;

public class CernSecurityBlockingError extends RuntimeException {

private static final long serialVersionUID = 1L;

public CernSecurityBlockingError(String message) {
super(message);
}

public CernSecurityBlockingError(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ public Optional<VOPersonDTO> getHrDbPersonRecord(String personId) {
new HttpEntity<>(buildAuthHeaders()), VOPersonDTO.class);
return Optional.of(response.getBody());
} catch (RestClientException e) {
if ((e instanceof HttpClientErrorException)
&& (((HttpClientErrorException) e).getStatusCode().equals(NOT_FOUND))) {
if (e instanceof HttpClientErrorException httpException
&& httpException.getStatusCode().equals(NOT_FOUND)) {
return Optional.empty();
}
throw new CernHrDbApiError(e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.api.registration.cern;

import java.util.Optional;
import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import it.infn.mw.iam.api.registration.cern.dto.VOPersonDTO;
import it.infn.mw.iam.api.registration.cern.dto.CernTokenResponse;
import it.infn.mw.iam.authn.oidc.RestTemplateFactory;
import it.infn.mw.iam.config.cern.CernProperties;

@Service
@Profile("cern")
public class DefaultCernSecurityBlockingService implements CernSecurityBlockingApiService {

public static final Logger LOG = LoggerFactory.getLogger(DefaultCernSecurityBlockingService.class);

public static final String QUERY_API_PATH = "/api/v1.0/Identity/-/Query";

Check warning on line 45 in iam-login-service/src/main/java/it/infn/mw/iam/api/registration/cern/DefaultCernSecurityBlockingService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor your code to get this URI from a customizable parameter.

See more on https://sonarcloud.io/project/issues?id=indigo-iam_iam&issues=AZxRonzAlAD_yq1gUy8k&open=AZxRonzAlAD_yq1gUy8k&pullRequest=1156

private String cachedToken;
private Instant tokenExpiry;
final RestTemplateFactory rtFactory;
final CernProperties properties;

public DefaultCernSecurityBlockingService(RestTemplateFactory rtFactory, CernProperties properties) {
this.rtFactory = rtFactory;
this.properties = properties;
}

private HttpHeaders buildAuthHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getAccessToken());
return headers;
}

private String getAccessToken() {

Instant now = Instant.now();

if (cachedToken != null && tokenExpiry != null && now.isBefore(tokenExpiry)) {
LOG.debug("Using cached access token, expires at {}", tokenExpiry);
return cachedToken;
}
LOG.debug("Requesting new access token from CERN Security Blocking API");
RestTemplate rt = rtFactory.newRestTemplate();


HttpHeaders headers = new HttpHeaders();
headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.add("grant_type", "client_credentials");
form.add("client_id", properties.getBlocking().getClientId());
form.add("client_secret", properties.getBlocking().getClientSecret());
form.add("audience", properties.getBlocking().getAudience());

HttpEntity<MultiValueMap<String, String>> request =
new HttpEntity<>(form, headers);

LOG.debug("Requesting access token with client_id: {}, audience: {}, token_url: {}", properties.getBlocking().getClientId(), properties.getBlocking().getAudience(), properties.getBlocking().getTokenUrl());
try {
ResponseEntity<CernTokenResponse> response = rt.exchange(
properties.getBlocking().getTokenUrl(),
HttpMethod.POST,
request,
CernTokenResponse.class
);
CernTokenResponse body = response.getBody();
if (body == null) {
LOG.warn("CERN security blocking token endpoint returned empty body");
throw new CernSecurityBlockingError("CERN security blocking token endpoint returned empty body");
}

cachedToken = body.getAccessToken();
LOG.debug("Received new access token, expires in {} seconds", body.getExpiresIn());

tokenExpiry = now.plusSeconds(body.getExpiresIn() - properties.getBlocking().getGracePeriod());
} catch (RestClientException e) {
throw new CernSecurityBlockingError("Error fetching security blocking api access token: " + e.getMessage(), e);
}


return cachedToken;
}

@Override
public Optional<VOPersonDTO> getSecurityBlockingRecord(String personId) {
RestTemplate restTemplate = rtFactory.newRestTemplate();

String url = String.format("%s%s", properties.getBlocking().getAuthorizationUrl(),
QUERY_API_PATH);

LOG.debug("Checking security blocking for person {} with query at URL {}", personId, url);

String data = String.format("{\"operator\":\"Equals\",\"value\":\"%s\",\"property\":\"personId\"}",
personId);

HttpHeaders headers = buildAuthHeaders();
headers.setContentType(org.springframework.http.MediaType.valueOf("application/json-patch+json"));

try {
ResponseEntity<VOPersonDTO> response = restTemplate.exchange(
url,
HttpMethod.POST,
new HttpEntity<>(data, headers),
VOPersonDTO.class
);
return Optional.ofNullable(response.getBody());
} catch (HttpClientErrorException.NotFound e) {
return Optional.empty();
} catch (RestClientException e) {
throw new CernSecurityBlockingError("Error fetching security blocking record: " + e.getMessage(), e);
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.api.registration.cern.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public class CernTokenResponse {

@JsonProperty("access_token")
private String accessToken;

@JsonProperty("expires_in")
private long expiresIn;

@JsonProperty("token_type")
private String tokenType;

public String getAccessToken() {
return accessToken;
}

public long getExpiresIn() {
return expiresIn;
}

public String getTokenType() {
return tokenType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class VOPersonDTO {
private String email;
private String physicalEmail;
private Set<ParticipationDTO> participations;
private boolean blocked;

public Long getId() {
return id;
Expand Down Expand Up @@ -177,4 +178,13 @@ public Set<ParticipationDTO> getParticipations() {
public void setParticipations(Set<ParticipationDTO> participations) {
this.participations = participations;
}

public boolean getBlocked() {
return blocked;
}

public void setBlocked(boolean blocked) {
this.blocked = blocked;
}

}
Loading
Loading