Skip to content

Commit

Permalink
SHIBUI-2393
Browse files Browse the repository at this point in the history
correcting logic for testing enable
  • Loading branch information
chasegawa committed Dec 5, 2022
1 parent 634feb5 commit 13d4c79
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,7 @@ public IShibUiPermissionEvaluator shibUiPermissionEvaluator(EntityDescriptorRepo
public DynamicRegistrationService dynamicRegistrationService(DynamicRegistrationInfoRepository driRepo, OwnershipRepository ownershipRepo,
IShibUiPermissionEvaluator permissionEvaluator, UserService userService, IGroupService groupService,
@Qualifier("shibUIConfiguration") ShibUIConfiguration config, RestTemplateBuilder restTemplateBuilder) {
URL idpUrl = config.getShibIdpServer();
RestTemplate template = restTemplateBuilder.build();
ShibRestTemplateDelegate delegate = new ShibRestTemplateDelegate(idpUrl, template);
ShibRestTemplateDelegate delegate = new ShibRestTemplateDelegate(config);
return new JPADynamicRegistrationServiceImpl(groupService, driRepo, ownershipRepo, delegate, permissionEvaluator, userService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,35 @@

import lombok.Getter;
import lombok.Setter;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.URL;
import java.net.http.HttpClient;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -45,4 +69,25 @@ public class ShibUIConfiguration {
* The URL of the shib idp server ala - https://idp.someschool.edu/idp
*/
private URL shibIdpServer;

private RestTemplate restTemplate;

@Profile("very-dangerous")
@Bean
public RestTemplate dangerousRestTemplate() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});

TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public ResponseEntity<?> enableDynamicRegistration(@PathVariable String resource
HttpStatus status = dynamicRegistrationService.enableDynamicRegistration(resourceId);
switch (status) {
case OK:
case ACCEPTED: return ResponseEntity.ok("Service enabled");
case CREATED: return ResponseEntity.ok("Service enabled");
case NOT_FOUND: throw new UnsupportedShibUiOperationException("Request returned NOT FOUND, please contact a system admin to check configuration");
case FORBIDDEN: throw new ForbiddenException("Request was denied with FORBIDDEN, please contact a system admin to check configuration");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class DynamicRegistrationRepresentation {
private String tokenEndpointAuthMethod;
private String tosUri;
private int version;
private String clientId;

public DynamicRegistrationRepresentation(DynamicRegistrationInfo dri) {
applicationType = dri.getApplicationType();
Expand All @@ -59,6 +60,7 @@ public DynamicRegistrationRepresentation(DynamicRegistrationInfo dri) {
tokenEndpointAuthMethod = dri.getTokenEndpointAuthMethod();
tosUri = dri.getTosUri();
version = dri.hashCode();
clientId = dri.getClientId();
}

public DynamicRegistrationInfo buildDynamicRegistrationInfo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class DynamicRegistrationInfo extends AbstractAuditable implements Ownabl
private String subjectType;
private String tokenEndpointAuthMethod;
private String tosUri;
private String clientId;

@ElementCollection(fetch = FetchType.EAGER)
@EqualsAndHashCode.Exclude
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ public HttpStatus enableDynamicRegistration(String resourceId) throws Persistent
if (!shibUiAuthorizationDelegate.hasPermission(userService.getCurrentUserAuthentication(), existingDri, PermissionType.enable)) {
throw new ForbiddenException("You do not have the permissions necessary to enable this service");
}
return shibRestTemplateDelegate.sendRequest(existingDri);
HttpStatus status = shibRestTemplateDelegate.sendRequest(existingDri);
if (status == HttpStatus.CREATED || status == HttpStatus.OK) {
existingDri.setEnabled(true);
repository.save(existingDri);
}
return status;
}

private boolean entityExists(String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.internet2.tier.shibboleth.admin.ui.configuration.ShibUIConfiguration;
import edu.internet2.tier.shibboleth.admin.ui.domain.oidc.DynamicRegistrationInfo;
import edu.internet2.tier.shibboleth.admin.ui.exception.UnsupportedShibUiOperationException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -15,55 +18,79 @@
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
* Requires that the shib server url be non-null
* Requires that the shib server url be non-null. The URL of the shib idp server ala - https://idp.someschool.edu/idp
* The URL is used to both fetch a token from Shib as well as to call the OIDC plugin
*/
public class ShibRestTemplateDelegate {
private URL shibUrl;
ShibUIConfiguration config;
private URL tokenURL;
private URL registrationURL;
private RestTemplate restTemplate;

public ShibRestTemplateDelegate(URL url, RestTemplate template) {
this.restTemplate = template;
public ShibRestTemplateDelegate(ShibUIConfiguration config) {
this.config = config;
URL url = config.getShibIdpServer();
if (url != null) {
try {
shibUrl = new URL(url.toExternalForm() + "/profile/oidc/register");
registrationURL = new URL(url.toExternalForm() + "/profile/oidc/register");
tokenURL = new URL(url.toExternalForm() + "/profile/admin/oidc/issue-registration-access-token?policyId=shibboleth.DefaultRelyingParty");
}
catch (MalformedURLException e) {
shibUrl = null;
tokenURL = null;
registrationURL = null;
}
}
}

/**
* Handles sending the Dynamic Registration request to Shibboleth (assuming the URL for Shib was configured for this)
* @throws UnsupportedShibUiOperationException
*/
public HttpStatus sendRequest(DynamicRegistrationInfo dri) throws UnsupportedShibUiOperationException {
if (shibUrl == null) {
if (config.getRestTemplate() != null) {
this.restTemplate = config.getRestTemplate();
}

if (tokenURL == null) {
throw new UnsupportedShibUiOperationException("Dynamic Registration endpoint not configured properly, please contact your system admin.");
}
try {
ResponseEntity response = restTemplate.postForEntity(shibUrl.toURI(), convertDynamicReg(dri), Map.class);
// Fetch an access token from SHIBBOLETH
ResponseEntity tokenResponse = restTemplate.postForEntity(tokenURL.toURI(), "", Map.class);
String token = ((Map)tokenResponse.getBody()).get("access_token").toString();

HttpEntity<String> entity = convertDynamicReg(dri, token);
ResponseEntity response = restTemplate.exchange(registrationURL.toURI(), HttpMethod.POST, entity, Map.class);
if (response.getStatusCode() == HttpStatus.CREATED) {
dri.setClientId(((Map)response.getBody()).get("client_id").toString());
}
return response.getStatusCode();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}

private Object convertDynamicReg(DynamicRegistrationInfo dri) {
private HttpEntity<String> convertDynamicReg(DynamicRegistrationInfo dri, String token) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + token);
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // skip any null values

Map<String, String> valuesMap = new HashMap<>();
valuesMap.put("redirect_uris", dri.getRedirectUris());
valuesMap.put("response_types", dri.getResponseTypes());
valuesMap.put("grant_types", dri.getGrantType().name());
Map<String, Object> valuesMap = new HashMap<>();
valuesMap.put("redirect_uris", arrayOrNull(dri.getRedirectUris()));
valuesMap.put("response_types", arrayOrNull(dri.getResponseTypes()));
valuesMap.put("grant_types", dri.getGrantType());
valuesMap.put("application_type", dri.getApplicationType());
valuesMap.put("contacts", dri.getContacts());
valuesMap.put("contacts", arrayOrNull(dri.getContacts()));
valuesMap.put("subject_type", dri.getSubjectType());
valuesMap.put("jwks", dri.getJwks());
valuesMap.put("jwks", StringUtils.defaultIfEmpty(dri.getJwks(), null));
valuesMap.put("token_endpoint_auth_method", dri.getTokenEndpointAuthMethod());
valuesMap.put("logo_uri", dri.getLogoUri());
valuesMap.put("policy_uri", dri.getPolicyUri());
Expand All @@ -79,4 +106,11 @@ private Object convertDynamicReg(DynamicRegistrationInfo dri) {
}
return new HttpEntity<String>(json, headers);
}

private String[] arrayOrNull(String values) {
if (values == null) {
return null;
}
return values.split(" ");
}
}

0 comments on commit 13d4c79

Please sign in to comment.