diff --git a/dmp-backend/web/pom.xml b/dmp-backend/web/pom.xml index 2331a130a..970310d07 100644 --- a/dmp-backend/web/pom.xml +++ b/dmp-backend/web/pom.xml @@ -120,8 +120,67 @@ org.springframework.boot spring-boot-starter-tomcat - + + org.opensaml + opensaml-core + ${opensaml.version} + compile + + + org.opensaml + opensaml-saml-api + ${opensaml.version} + compile + + + org.opensaml + opensaml-saml-impl + ${opensaml.version} + compile + + + org.opensaml + opensaml-soap-api + ${opensaml.version} + compile + + + org.opensaml + opensaml-xmlsec-api + ${opensaml.version} + + + org.opensaml + opensaml-security-api + ${opensaml.version} + + + org.opensaml + opensaml-security-impl + ${opensaml.version} + + + org.opensaml + opensaml-profile-api + ${opensaml.version} + + + org.opensaml + xmltooling + 1.4.4 + + + jakarta.xml.soap + jakarta.xml.soap-api + 3.0.0 + + + com.sun.xml.messaging.saaj + saaj-impl + 3.0.0-M2 + + devel @@ -198,5 +257,39 @@ + + ${project.artifactId}-${project.version} + + + src/main/ui-resources + + node_modules/** + + + + src/main/resources + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + eu.eudat.EuDatApplication + 4.0.1 + \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java index 8a69c2c50..e22d5b797 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java @@ -140,7 +140,7 @@ public class Admin extends BaseController { @RequestMapping(method = RequestMethod.POST, value = {"/upload", "/upload/{id}"}) public ResponseEntity setDatasetProfileXml(@RequestParam("file") MultipartFile file, - @PathVariable(value = "id") String id, + @PathVariable(value = "id", required = false) String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws Exception { eu.eudat.logic.utilities.documents.xml.datasetProfileXml.datasetProfileModel.DatasetProfile datasetProfileModel = this.datasetProfileManager.createDatasetProfileFromXml(file); eu.eudat.models.data.admin.composite.DatasetProfile datasetProfileEntity = datasetProfileModel.toAdminCompositeModel(file.getOriginalFilename()); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Saml2PostBinding.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Saml2PostBinding.java new file mode 100644 index 000000000..061617e0c --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Saml2PostBinding.java @@ -0,0 +1,52 @@ +package eu.eudat.controllers; + +import eu.eudat.logic.security.CustomAuthenticationProvider; +import eu.eudat.logic.security.validators.TokenValidatorFactoryImpl; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.models.data.login.LoginInfo; +import eu.eudat.models.data.principal.PrincipalModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@CrossOrigin +@RequestMapping(value = {"/api/auth/saml2"}) +public class Saml2PostBinding extends BaseController { + + private CustomAuthenticationProvider customAuthenticationProvider; + + @Autowired + public Saml2PostBinding(ApiContext apiContext, CustomAuthenticationProvider customAuthenticationProvider) { + super(apiContext); + this.customAuthenticationProvider = customAuthenticationProvider; + } + + @RequestMapping(method = RequestMethod.POST, value = {"/postBinding"}, consumes = "application/x-www-form-urlencoded") + public @ResponseBody + ResponseEntity verify(@RequestParam(value = "SAMLResponse") String SAMLResponse, @RequestParam(value = "RelayState") String RelayState) throws GeneralSecurityException { + + Map map = Arrays.stream(RelayState.split("&")).map(s -> s.split("=")).collect(Collectors.toMap(e -> e[0], e -> e[1])); + + LoginInfo loginInfo = new LoginInfo(); + loginInfo.setTicket(SAMLResponse); + loginInfo.setProvider(TokenValidatorFactoryImpl.LoginProvider.CONFIGURABLE.getValue()); + Map providerId = new HashMap<>(); + providerId.put("configurableLoginId", map.get("configurableLoginId")); + loginInfo.setData(providerId); + + PrincipalModel principal = this.customAuthenticationProvider.authenticate(loginInfo); + + return ResponseEntity.status(HttpStatus.FOUND).header(HttpHeaders.LOCATION, "http://localhost:4200/login/external/saml?token=" + principal.getToken().toString()).build(); + + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index d0d051375..bdc9b1826 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -97,7 +97,7 @@ import java.util.stream.Stream; @Component public class DataManagementPlanManager { private static final Logger logger = LoggerFactory.getLogger(DataManagementPlanManager.class); - private static final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper; private final Map notificationPaths = Stream.of(new Object[][] { {NotificationType.DMP_MODIFIED, "/plans/edit"}, @@ -125,6 +125,7 @@ public class DataManagementPlanManager { this.userManager = userManager; this.metricsManager = metricsManager; this.configLoader = configLoader; + this.objectMapper = new ObjectMapper(); } /* @@ -1232,10 +1233,11 @@ public class DataManagementPlanManager { .filter(item -> item.getStatus() != Dataset.Status.CANCELED.getValue()) .filter(item -> item.getStatus() != Dataset.Status.DELETED.getValue()) .filter(item -> !isPublic && !isFinalized || item.getStatus() == Dataset.Status.FINALISED.getValue()) + .sorted(Comparator.comparing(Dataset::getCreated)) .forEach(datasetEntity -> { Map properties = new HashMap<>(); if (datasetEntity.getProperties() != null) { - ObjectMapper objectMapper = new ObjectMapper(); + //ObjectMapper objectMapper = new ObjectMapper(); try { properties = objectMapper.readValue(datasetEntity.getProperties(), LinkedHashMap.class); } catch (IOException e) { @@ -1691,7 +1693,7 @@ public class DataManagementPlanManager { dm.setAssociatedUsers(associatedUsers); // Sets associatedUsers property. dm.setDynamicFields(dynamicFields); // Sets dynamicFields property. dm.setDefinition(dmpProfile); - ObjectMapper mapper = new ObjectMapper(); + //ObjectMapper mapper = new ObjectMapper(); Map extraPropertiesMap = new HashMap<>(); if (dataManagementPlans.get(0).getLanguage() != null) { extraPropertiesMap.put("language", dataManagementPlans.get(0).getLanguage()); @@ -1703,7 +1705,7 @@ public class DataManagementPlanManager { extraPropertiesMap.put("publicDate", dataManagementPlans.get(0).getPublicDate()); } if (dataManagementPlans.get(0).getCosts() != null && !dataManagementPlans.get(0).getCosts().isEmpty()) { - extraPropertiesMap.put("costs", mapper.readValue(dataManagementPlans.get(0).getCosts(), ArrayList.class)); + extraPropertiesMap.put("costs", objectMapper.readValue(dataManagementPlans.get(0).getCosts(), ArrayList.class)); } dm.setExtraProperties(extraPropertiesMap); @@ -1734,7 +1736,7 @@ public class DataManagementPlanManager { } catch (Exception ignored) { dataset.setProfile(databaseRepository.getDatasetProfileDao().find(associatedProfiles.get(0).getId())); } - dataset.setProperties(new ObjectMapper().writeValueAsString(das.getFieldImportModels())); + dataset.setProperties(objectMapper.writeValueAsString(das.getFieldImportModels())); dataset.setStatus((short) 0); dataset.setRegistries(new HashSet<>()); dataset.setDatasetDataRepositories(new HashSet<>()); @@ -2162,8 +2164,8 @@ public class DataManagementPlanManager { } return finalDoi; } catch (HttpClientErrorException | HttpServerErrorException ex) { - ObjectMapper ob = new ObjectMapper(); - Map parsedException = ob.readValue(ex.getResponseBodyAsString(), HashMap.class); + //ObjectMapper ob = new ObjectMapper(); + Map parsedException = objectMapper.readValue(ex.getResponseBodyAsString(), HashMap.class); throw new IOException(parsedException.get("message"), ex); } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/CustomAuthenticationProvider.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/CustomAuthenticationProvider.java index a068a5dcb..f6f7b8be9 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/CustomAuthenticationProvider.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/CustomAuthenticationProvider.java @@ -7,6 +7,8 @@ import eu.eudat.models.data.login.LoginInfo; import eu.eudat.models.data.principal.PrincipalModel; import eu.eudat.models.data.security.Principal; import eu.eudat.logic.security.validators.TokenValidatorFactory; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; +import net.shibboleth.utilities.java.support.resolver.ResolverException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,7 +29,7 @@ public class CustomAuthenticationProvider { String token = credentials.getTicket(); try { Principal principal = this.tokenValidatorFactory.getProvider(credentials.getProvider()).validateToken(credentials); - return PrincipalModel.fromEntity(principal); + return (principal != null) ? PrincipalModel.fromEntity(principal) : null; } catch (NonValidTokenException e) { logger.error("Could not validate a user by his token! Reason: " + e.getMessage(), e); throw new UnauthorisedException("Token validation failed - Not a valid token"); @@ -37,6 +39,9 @@ public class CustomAuthenticationProvider { } catch (NullEmailException e) { logger.error(e.getMessage(), e); throw new NullEmailException(); + } catch (ResolverException | ComponentInitializationException e){ + logger.error(e.getMessage(), e); + throw new GeneralSecurityException(); } } } \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProvider.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProvider.java index 0f29de3fa..4474703ec 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProvider.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProvider.java @@ -1,11 +1,11 @@ package eu.eudat.logic.security.customproviders.ConfigurableProvider; -import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviderUserSettings; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProviderUserSettings; import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderResponseToken; public interface ConfigurableProviderCustomProvider { ConfigurableProviderResponseToken getAccessToken(String code, String redirectUri, String clientId, String clientSecret, String accessTokenUrl, String grantType, String access_token, String expires_in); - ConfigurableProviderUser getUser(String accessToken, ConfigurableProviderUserSettings user); + ConfigurableProviderUser getUser(String accessToken, Oauth2ConfigurableProviderUserSettings user); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProviderImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProviderImpl.java index 1ebf8c336..eec2b4141 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProviderImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProviderImpl.java @@ -1,6 +1,6 @@ package eu.eudat.logic.security.customproviders.ConfigurableProvider; -import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviderUserSettings; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProviderUserSettings; import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderResponseToken; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -43,7 +43,7 @@ public class ConfigurableProviderCustomProviderImpl implements ConfigurableProvi } @Override - public ConfigurableProviderUser getUser(String accessToken, ConfigurableProviderUserSettings user) { + public ConfigurableProviderUser getUser(String accessToken, Oauth2ConfigurableProviderUserSettings user) { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = this.createBearerAuthHeaders(accessToken); HttpEntity entity = new HttpEntity<>(headers); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderUser.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderUser.java index 111abad14..199fe2485 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderUser.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderUser.java @@ -1,6 +1,6 @@ package eu.eudat.logic.security.customproviders.ConfigurableProvider; -import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviderUserSettings; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProviderUserSettings; import java.util.Map; @@ -30,7 +30,7 @@ public class ConfigurableProviderUser { this.email = email; } - ConfigurableProviderUser getConfigurableProviderUser(Map data, ConfigurableProviderUserSettings user) { + ConfigurableProviderUser getConfigurableProviderUser(Map data, Oauth2ConfigurableProviderUserSettings user) { if (user.getId() != null && !user.getId().isEmpty()) this.id = (String) data.get(user.getId()); if (user.getName() != null && !user.getName().isEmpty()) diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProvider.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProvider.java index 2e62f7007..7599de3e5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProvider.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProvider.java @@ -1,22 +1,23 @@ package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; +import com.fasterxml.jackson.annotation.*; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.Saml2ConfigurableProvider; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Oauth2ConfigurableProvider.class, name = "oauth2"), + @JsonSubTypes.Type(value = Saml2ConfigurableProvider.class, name = "saml2") +}) public class ConfigurableProvider { private boolean enabled; private String configurableLoginId; + private String type; private String name; - private String clientId; - private String clientSecret; - private String redirect_uri; - private String access_token_url; - private String grant_type; - private ConfigurableProviderToken token; - private ConfigurableProviderUserSettings user; - private String oauthUrl; - private String scope; - private String state; + private String logoUrl; - public boolean getEnabled() { + public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { @@ -30,6 +31,13 @@ public class ConfigurableProvider { this.configurableLoginId = configurableLoginId; } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public String getName() { return name; } @@ -37,73 +45,11 @@ public class ConfigurableProvider { this.name = name; } - public String getClientId() { - return clientId; + public String getLogoUrl() { + return logoUrl; } - public void setClientId(String clientId) { - this.clientId = clientId; + public void setLogoUrl(String logoUrl) { + this.logoUrl = logoUrl; } - public String getClientSecret() { - return clientSecret; - } - public void setClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - } - - public String getRedirect_uri() { - return redirect_uri; - } - public void setRedirect_uri(String redirect_uri) { - this.redirect_uri = redirect_uri; - } - - public String getAccess_token_url() { - return access_token_url; - } - public void setAccess_token_url(String access_token_url) { - this.access_token_url = access_token_url; - } - - public String getGrant_type() { - return grant_type; - } - public void setGrant_type(String grant_type) { - this.grant_type = grant_type; - } - - public ConfigurableProviderToken getToken() { - return token; - } - public void setToken(ConfigurableProviderToken token) { - this.token = token; - } - - public ConfigurableProviderUserSettings getUser() { - return user; - } - public void setUser(ConfigurableProviderUserSettings user) { - this.user = user; - } - - public String getOauthUrl() { - return oauthUrl; - } - public void setOauthUrl(String oauthUrl) { - this.oauthUrl = oauthUrl; - } - - public String getScope() { - return scope; - } - public void setScope(String scope) { - this.scope = scope; - } - - public String getState() { - return state; - } - public void setState(String state) { - this.state = state; - } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProvider.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProvider.java new file mode 100644 index 000000000..0e620d556 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProvider.java @@ -0,0 +1,88 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; + +public class Oauth2ConfigurableProvider extends ConfigurableProvider { + + private String clientId; + private String clientSecret; + private String redirect_uri; + private String access_token_url; + private String grant_type; + private Oauth2ConfigurableProviderToken token; + private Oauth2ConfigurableProviderUserSettings user; + private String oauthUrl; + private String scope; + private String state; + + public String getClientId() { + return clientId; + } + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getRedirect_uri() { + return redirect_uri; + } + public void setRedirect_uri(String redirect_uri) { + this.redirect_uri = redirect_uri; + } + + public String getAccess_token_url() { + return access_token_url; + } + public void setAccess_token_url(String access_token_url) { + this.access_token_url = access_token_url; + } + + public String getGrant_type() { + return grant_type; + } + public void setGrant_type(String grant_type) { + this.grant_type = grant_type; + } + + public Oauth2ConfigurableProviderToken getToken() { + return token; + } + public void setToken(Oauth2ConfigurableProviderToken token) { + this.token = token; + } + + public Oauth2ConfigurableProviderUserSettings getUser() { + return user; + } + public void setUser(Oauth2ConfigurableProviderUserSettings user) { + this.user = user; + } + + public String getOauthUrl() { + return oauthUrl; + } + public void setOauthUrl(String oauthUrl) { + this.oauthUrl = oauthUrl; + } + + public String getScope() { + return scope; + } + public void setScope(String scope) { + this.scope = scope; + } + + public String getState() { + return state; + } + public void setState(String state) { + this.state = state; + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderToken.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProviderToken.java similarity index 85% rename from dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderToken.java rename to dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProviderToken.java index ac2021a82..f03235263 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderToken.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProviderToken.java @@ -1,6 +1,6 @@ -package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2; -public class ConfigurableProviderToken { +public class Oauth2ConfigurableProviderToken { private String access_token; private String expires_in; diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderUserSettings.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProviderUserSettings.java similarity index 89% rename from dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderUserSettings.java rename to dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProviderUserSettings.java index 693957ca3..cf639c1bc 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderUserSettings.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/oauth2/Oauth2ConfigurableProviderUserSettings.java @@ -1,6 +1,6 @@ -package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2; -public class ConfigurableProviderUserSettings { +public class Oauth2ConfigurableProviderUserSettings { private String id; private String name; private String email; diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/saml2/Saml2ConfigurableProvider.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/saml2/Saml2ConfigurableProvider.java new file mode 100644 index 000000000..ed3ddfbf7 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/saml2/Saml2ConfigurableProvider.java @@ -0,0 +1,255 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2; + +import com.fasterxml.jackson.annotation.JsonValue; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; + +import java.util.Map; + +public class Saml2ConfigurableProvider extends ConfigurableProvider { + + public enum SAML2UsingFormat { + NAME("name"), FRIENDLY_NAME("friendly_name"); + + private String name; + SAML2UsingFormat(String name) { + this.name = name; + } + @JsonValue + public String getName() { return name; } + + public static SAML2UsingFormat fromName(String name) { + for (SAML2UsingFormat type: SAML2UsingFormat.values()) { + if (name.equals(type.getName())) { + return type; + } + } + throw new IllegalArgumentException("Unsupported SAML2 Attribute " + name); + } + } + + public enum SAML2AttributeType { + XSSTRING("XSString"), XSINTEGER("XSInteger"), XSDATETIME("XSDateTime"), XSBOOLEAN("XSBoolean"), XSBASE64BINARY("XSBase64Binary"), XSURI("XSURI"), XSQNAME("XSQName"), XSANY("XSAny"); + + private String type; + SAML2AttributeType(String type) { + this.type = type; + } + @JsonValue + public String getType() { return type; } + + public static SAML2AttributeType fromType(String type) { + for (SAML2AttributeType t: SAML2AttributeType.values()) { + if (type.equals(t.getType())) { + return t; + } + } + throw new IllegalArgumentException("Unsupported SAML2 Attribute Type " + type); + } + } + + public enum KeyFormat { + JKS("JKS"), PKCS12("PKCS12"); + + private String type; + KeyFormat(String type) { + this.type = type; + } + @JsonValue + public String getType() { return type; } + + public static KeyFormat fromType(String type) { + for (KeyFormat t: KeyFormat.values()) { + if (type.equals(t.getType())) { + return t; + } + } + throw new IllegalArgumentException("Unsupported Keystore format " + type); + } + } + + private String spEntityId; + private String idpEntityId; + private String idpUrl; + private String idpArtifactUrl; + private String idpMetadataUrl; + private boolean assertionEncrypted; + private KeyFormat keyFormat; + private String keyAlias; + private String credentialPath; + private String archivePassword; + private String keyPassword; + private boolean responseSigned; + private boolean assertionSigned; + private boolean signatureRequired; + private String signatureKeyAlias; + private String signaturePath; + private String signatureKeyStorePassword; + private String signatureKeyPassword; + private SAML2UsingFormat usingFormat; + private Map attributeTypes; + private Map configurableUserFromAttributes; + private String binding; + private String assertionConsumerServiceUrl; + + public String getSpEntityId() { + return spEntityId; + } + public void setSpEntityId(String spEntityId) { + this.spEntityId = spEntityId; + } + + public String getIdpEntityId() { + return idpEntityId; + } + public void setIdpEntityId(String idpEntityId) { + this.idpEntityId = idpEntityId; + } + + public String getIdpUrl() { + return idpUrl; + } + public void setIdpUrl(String idpUrl) { + this.idpUrl = idpUrl; + } + + public String getIdpArtifactUrl() { + return idpArtifactUrl; + } + public void setIdpArtifactUrl(String idpArtifactUrl) { + this.idpArtifactUrl = idpArtifactUrl; + } + + public String getIdpMetadataUrl() { + return idpMetadataUrl; + } + public void setIdpMetadataUrl(String idpMetadataUrl) { + this.idpMetadataUrl = idpMetadataUrl; + } + + public boolean isAssertionEncrypted() { + return assertionEncrypted; + } + public void setAssertionEncrypted(boolean assertionEncrypted) { + this.assertionEncrypted = assertionEncrypted; + } + + public KeyFormat getKeyFormat() { + return keyFormat; + } + public void setKeyFormat(KeyFormat keyFormat) { + this.keyFormat = keyFormat; + } + + public String getKeyAlias() { + return keyAlias; + } + public void setKeyAlias(String keyAlias) { + this.keyAlias = keyAlias; + } + + public String getCredentialPath() { + return credentialPath; + } + public void setCredentialPath(String credentialPath) { + this.credentialPath = credentialPath; + } + + public String getArchivePassword() { + return archivePassword; + } + public void setArchivePassword(String archivePassword) { + this.archivePassword = archivePassword; + } + + public String getKeyPassword() { + return keyPassword; + } + public void setKeyPassword(String keyPassword) { + this.keyPassword = keyPassword; + } + + public boolean isResponseSigned() { + return responseSigned; + } + public void setResponseSigned(boolean responseSigned) { + this.responseSigned = responseSigned; + } + + public boolean isAssertionSigned() { + return assertionSigned; + } + public void setAssertionSigned(boolean assertionSigned) { + this.assertionSigned = assertionSigned; + } + + public boolean isSignatureRequired() { + return signatureRequired; + } + public void setSignatureRequired(boolean signatureRequired) { + this.signatureRequired = signatureRequired; + } + + public String getSignatureKeyAlias() { + return signatureKeyAlias; + } + public void setSignatureKeyAlias(String signatureKeyAlias) { + this.signatureKeyAlias = signatureKeyAlias; + } + + public String getSignaturePath() { + return signaturePath; + } + public void setSignaturePath(String signaturePath) { + this.signaturePath = signaturePath; + } + + public String getSignatureKeyStorePassword() { + return signatureKeyStorePassword; + } + public void setSignatureKeyStorePassword(String signatureKeyStorePassword) { + this.signatureKeyStorePassword = signatureKeyStorePassword; + } + + public String getSignatureKeyPassword() { + return signatureKeyPassword; + } + public void setSignatureKeyPassword(String signatureKeyPassword) { + this.signatureKeyPassword = signatureKeyPassword; + } + + public SAML2UsingFormat getUsingFormat() { + return usingFormat; + } + public void setUsingFormat(SAML2UsingFormat usingFormat) { + this.usingFormat = usingFormat; + } + + public Map getConfigurableUserFromAttributes() { + return configurableUserFromAttributes; + } + public void setConfigurableUserFromAttributes(Map configurableUserFromAttributes) { + this.configurableUserFromAttributes = configurableUserFromAttributes; + } + + public Map getAttributeTypes() { + return attributeTypes; + } + public void setAttributeTypes(Map attributeTypes) { + this.attributeTypes = attributeTypes; + } + + public String getBinding() { + return binding; + } + public void setBinding(String binding) { + this.binding = binding; + } + + public String getAssertionConsumerServiceUrl() { + return assertionConsumerServiceUrl; + } + public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) { + this.assertionConsumerServiceUrl = assertionConsumerServiceUrl; + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProviderModel.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProviderModel.java index b09b002d2..4df28c445 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProviderModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProviderModel.java @@ -5,12 +5,9 @@ import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.Con public class ConfigurableProviderModel { private String configurableLoginId; + private String type; private String name; - private String clientId; - private String redirect_uri; - private String oauthUrl; - private String scope; - private String state; + private String logoUrl; public String getConfigurableLoginId() { return configurableLoginId; @@ -19,6 +16,13 @@ public class ConfigurableProviderModel { this.configurableLoginId = configurableLoginId; } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public String getName() { return name; } @@ -26,51 +30,20 @@ public class ConfigurableProviderModel { this.name = name; } - public String getClientId() { - return clientId; + public String getLogoUrl() { + return logoUrl; } - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public String getRedirect_uri() { - return redirect_uri; - } - public void setRedirect_uri(String redirect_uri) { - this.redirect_uri = redirect_uri; - } - - public String getOauthUrl() { - return oauthUrl; - } - public void setOauthUrl(String oauthUrl) { - this.oauthUrl = oauthUrl; - } - - public String getScope() { - return scope; - } - public void setScope(String scope) { - this.scope = scope; - } - - public String getState() { - return state; - } - public void setState(String state) { - this.state = state; + public void setLogoUrl(String logoUrl) { + this.logoUrl = logoUrl; } public ConfigurableProviderModel fromDataModel(ConfigurableProvider entity) { ConfigurableProviderModel model = new ConfigurableProviderModel(); model.setConfigurableLoginId(entity.getConfigurableLoginId()); + model.setType(entity.getType()); model.setName(entity.getName()); - model.setClientId(entity.getClientId()); - model.setRedirect_uri(entity.getRedirect_uri()); - model.setOauthUrl(entity.getOauthUrl()); - model.setScope(entity.getScope()); - model.setState(entity.getState()); - + model.setLogoUrl(entity.getLogoUrl()); return model; } + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProvidersModel.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProvidersModel.java index 28514d6d4..0ddc12088 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProvidersModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProvidersModel.java @@ -2,6 +2,10 @@ package eu.eudat.logic.security.customproviders.ConfigurableProvider.models; import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviders; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.Saml2ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.models.oauth2.Oauth2ConfigurableProviderModel; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.models.saml2.Saml2ConfigurableProviderModel; import java.util.LinkedList; import java.util.List; @@ -21,8 +25,14 @@ public class ConfigurableProvidersModel { List providerModelList = new LinkedList<>(); if (entity != null) { for (ConfigurableProvider entityProvider : entity.getProviders()) { - if (entityProvider.getEnabled()) - providerModelList.add(new ConfigurableProviderModel().fromDataModel(entityProvider)); + if (entityProvider.isEnabled()){ + if(entityProvider instanceof Oauth2ConfigurableProvider) + providerModelList.add(new Oauth2ConfigurableProviderModel().fromDataModel(entityProvider)); + else if(entityProvider instanceof Saml2ConfigurableProvider) + providerModelList.add(new Saml2ConfigurableProviderModel().fromDataModel(entityProvider)); + else + providerModelList.add(new ConfigurableProviderModel().fromDataModel(entityProvider)); + } } } model.setProviders(providerModelList); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/oauth2/Oauth2ConfigurableProviderModel.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/oauth2/Oauth2ConfigurableProviderModel.java new file mode 100644 index 000000000..26c6aeb10 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/oauth2/Oauth2ConfigurableProviderModel.java @@ -0,0 +1,65 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.models.oauth2; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.models.ConfigurableProviderModel; + +public class Oauth2ConfigurableProviderModel extends ConfigurableProviderModel { + + private String clientId; + private String redirect_uri; + private String oauthUrl; + private String scope; + private String state; + + public String getClientId() { + return clientId; + } + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getRedirect_uri() { + return redirect_uri; + } + public void setRedirect_uri(String redirect_uri) { + this.redirect_uri = redirect_uri; + } + + public String getOauthUrl() { + return oauthUrl; + } + public void setOauthUrl(String oauthUrl) { + this.oauthUrl = oauthUrl; + } + + public String getScope() { + return scope; + } + public void setScope(String scope) { + this.scope = scope; + } + + public String getState() { + return state; + } + public void setState(String state) { + this.state = state; + } + + @Override + public Oauth2ConfigurableProviderModel fromDataModel(ConfigurableProvider entity) { + Oauth2ConfigurableProviderModel model = new Oauth2ConfigurableProviderModel(); + model.setConfigurableLoginId(entity.getConfigurableLoginId()); + model.setType(entity.getType()); + model.setName(entity.getName()); + model.setLogoUrl(entity.getLogoUrl()); + model.setClientId(((Oauth2ConfigurableProvider)entity).getClientId()); + model.setRedirect_uri(((Oauth2ConfigurableProvider)entity).getRedirect_uri()); + model.setOauthUrl(((Oauth2ConfigurableProvider)entity).getOauthUrl()); + model.setScope(((Oauth2ConfigurableProvider)entity).getScope()); + model.setState(((Oauth2ConfigurableProvider)entity).getState()); + return model; + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/saml2/Saml2ConfigurableProviderModel.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/saml2/Saml2ConfigurableProviderModel.java new file mode 100644 index 000000000..5ee488177 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/saml2/Saml2ConfigurableProviderModel.java @@ -0,0 +1,56 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.models.saml2; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.Saml2ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.models.ConfigurableProviderModel; + +public class Saml2ConfigurableProviderModel extends ConfigurableProviderModel { + + private String spEntityId; + private String idpUrl; + private String binding; + private String assertionConsumerServiceUrl; + + public String getSpEntityId() { + return spEntityId; + } + public void setSpEntityId(String spEntityId) { + this.spEntityId = spEntityId; + } + + public String getIdpUrl() { + return idpUrl; + } + public void setIdpUrl(String idpUrl) { + this.idpUrl = idpUrl; + } + + public String getBinding() { + return binding; + } + public void setBinding(String binding) { + this.binding = binding; + } + + public String getAssertionConsumerServiceUrl() { + return assertionConsumerServiceUrl; + } + public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) { + this.assertionConsumerServiceUrl = assertionConsumerServiceUrl; + } + + @Override + public Saml2ConfigurableProviderModel fromDataModel(ConfigurableProvider entity) { + Saml2ConfigurableProviderModel model = new Saml2ConfigurableProviderModel(); + model.setConfigurableLoginId(entity.getConfigurableLoginId()); + model.setType(entity.getType()); + model.setName(entity.getName()); + model.setLogoUrl(entity.getLogoUrl()); + model.setSpEntityId(((Saml2ConfigurableProvider)entity).getSpEntityId()); + model.setIdpUrl(((Saml2ConfigurableProvider)entity).getIdpUrl()); + model.setBinding(((Saml2ConfigurableProvider)entity).getBinding()); + model.setAssertionConsumerServiceUrl(((Saml2ConfigurableProvider)entity).getAssertionConsumerServiceUrl()); + return model; + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidator.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidator.java index f03ca04c8..01323f1e0 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidator.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidator.java @@ -4,12 +4,14 @@ import eu.eudat.exceptions.security.NonValidTokenException; import eu.eudat.exceptions.security.NullEmailException; import eu.eudat.models.data.login.LoginInfo; import eu.eudat.models.data.security.Principal; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; +import net.shibboleth.utilities.java.support.resolver.ResolverException; import java.io.IOException; import java.security.GeneralSecurityException; public interface TokenValidator { - Principal validateToken(LoginInfo credentials) throws NonValidTokenException, IOException, GeneralSecurityException, NullEmailException; + Principal validateToken(LoginInfo credentials) throws NonValidTokenException, IOException, GeneralSecurityException, NullEmailException, ResolverException, ComponentInitializationException; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/ConfigurableProviderTokenValidator.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/ConfigurableProviderTokenValidator.java index bf426f96e..b951aeb31 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/ConfigurableProviderTokenValidator.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/ConfigurableProviderTokenValidator.java @@ -6,6 +6,8 @@ import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.security.customproviders.ConfigurableProvider.ConfigurableProviderCustomProvider; import eu.eudat.logic.security.customproviders.ConfigurableProvider.ConfigurableProviderUser; import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.oauth2.Oauth2ConfigurableProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.Saml2ConfigurableProvider; import eu.eudat.logic.security.validators.TokenValidator; import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderRequest; import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderResponseToken; @@ -13,14 +15,22 @@ import eu.eudat.logic.services.operations.authentication.AuthenticationService; import eu.eudat.models.data.login.LoginInfo; import eu.eudat.models.data.loginprovider.LoginProviderUser; import eu.eudat.models.data.security.Principal; + +import org.opensaml.saml.saml2.core.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Component("configurableProviderTokenValidator") public class ConfigurableProviderTokenValidator implements TokenValidator { + private static final Logger logger = LoggerFactory.getLogger(ConfigurableProviderTokenValidator.class); + private ConfigurableProviderCustomProvider configurableProvider; private AuthenticationService nonVerifiedUserAuthenticationService; private ConfigLoader configLoader; @@ -32,7 +42,7 @@ public class ConfigurableProviderTokenValidator implements TokenValidator { } public ConfigurableProviderResponseToken getAccessToken(ConfigurableProviderRequest configurableProviderRequest) { - ConfigurableProvider provider = getConfigurableProviderFromId(configurableProviderRequest.getConfigurableLoginId()); + Oauth2ConfigurableProvider provider = (Oauth2ConfigurableProvider)getConfigurableProviderFromId(configurableProviderRequest.getConfigurableLoginId()); return this.configurableProvider.getAccessToken(configurableProviderRequest.getCode(), provider.getRedirect_uri(), provider.getClientId(), provider.getClientSecret(), provider.getAccess_token_url(), provider.getGrant_type(), provider.getToken().getAccess_token(), provider.getToken().getExpires_in()); @@ -41,15 +51,83 @@ public class ConfigurableProviderTokenValidator implements TokenValidator { public Principal validateToken(LoginInfo credentials) throws NullEmailException { String configurableLoginId = ((Map) credentials.getData()).get("configurableLoginId").toString(); ConfigurableProvider configurableProvider = getConfigurableProviderFromId(configurableLoginId); - ConfigurableProviderUser configurableUser = this.configurableProvider.getUser(credentials.getTicket(), configurableProvider.getUser()); - LoginProviderUser user = new LoginProviderUser(); - user.setId(configurableUser.getId()); - user.setEmail(configurableUser.getEmail()); - user.setName(configurableUser.getName()); - user.setProvider(credentials.getProvider()); - user.setSecret(credentials.getTicket()); - return this.nonVerifiedUserAuthenticationService.Touch(user); + LoginProviderUser user = new LoginProviderUser(); + if (configurableProvider.getType().equals("oauth2")) { + ConfigurableProviderUser configurableUser = this.configurableProvider.getUser(credentials.getTicket(), ((Oauth2ConfigurableProvider)configurableProvider).getUser()); + user.setId(configurableUser.getId()); + user.setEmail(configurableUser.getEmail()); + user.setName(configurableUser.getName()); + user.setProvider(credentials.getProvider()); + user.setSecret(credentials.getTicket()); + return this.nonVerifiedUserAuthenticationService.Touch(user); + } + else if (configurableProvider.getType().equals("saml2")) { + + Assertion saml2Assertion = null; + try { + Saml2ConfigurableProvider saml2Provider = (Saml2ConfigurableProvider)configurableProvider; + if(saml2Provider.getBinding().equals("Redirect") || saml2Provider.getBinding().equals("Post")) + saml2Assertion = Saml2SSOUtils.processResponse(credentials.getTicket(), saml2Provider); + else if(saml2Provider.getBinding().equals("Artifact")) + saml2Assertion = Saml2SSOUtils.processArtifactResponse(credentials.getTicket(), saml2Provider); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + + if(saml2Assertion == null) + return null; + + List attributeStatements = saml2Assertion.getAttributeStatements(); + if(attributeStatements != null && !attributeStatements.isEmpty()){ + + List attributes = attributeStatements.get(0).getAttributes(); + if(attributes != null && !attributes.isEmpty()){ + + Saml2ConfigurableProvider.SAML2UsingFormat usingFormat = ((Saml2ConfigurableProvider)configurableProvider).getUsingFormat(); + Map attributeMapping = ((Saml2ConfigurableProvider)configurableProvider).getConfigurableUserFromAttributes(); + Map attributeType = ((Saml2ConfigurableProvider)configurableProvider).getAttributeTypes(); + Map saml2User = new HashMap<>(); + for(Attribute attribute: attributes){ + + String attributeName = Saml2SSOUtils.getAttributeName(attribute, usingFormat); + if(attributeName != null && attributeMapping.containsValue(attributeName)){ + + Saml2ConfigurableProvider.SAML2AttributeType attrType = attributeType.get(attributeName); + if(attribute.getAttributeValues() != null && !attribute.getAttributeValues().isEmpty() && attrType != null){ + Object attributeValue = Saml2SSOUtils.getAttributeType(attribute.getAttributeValues().get(0), attrType); + if(attributeValue != null) { + saml2User.put(attributeName, attributeValue); + } + } + + } + + } + + try{ + String subjectNameId = saml2Assertion.getSubject().getNameID().getValue(); + String userId = configurableLoginId + ": " + subjectNameId; + user.setId(userId); + } catch(NullPointerException e){ + logger.error("Could not get Subject NameID value of assertion"); + return null; + } + user.setEmail((String)saml2User.get(attributeMapping.get("email"))); + user.setName((String)saml2User.get(attributeMapping.get("name"))); + user.setProvider(credentials.getProvider()); + user.setSecret(credentials.getTicket()); + + } + + } + else + return null; + + return this.nonVerifiedUserAuthenticationService.Touch(user); + + } + return null; } private ConfigurableProvider getConfigurableProviderFromId(String configurableId) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/Saml2SSOUtils.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/Saml2SSOUtils.java new file mode 100644 index 000000000..b6f413dc6 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/Saml2SSOUtils.java @@ -0,0 +1,688 @@ +package eu.eudat.logic.security.validators.configurableProvider; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.Saml2ConfigurableProvider; +import jakarta.xml.soap.*; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; +import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import net.shibboleth.utilities.java.support.xml.BasicParserPool; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.xml.security.c14n.Canonicalizer; +import org.apache.xml.security.signature.XMLSignature; +import org.opensaml.core.config.ConfigurationService; +import org.opensaml.core.config.InitializationException; +import org.opensaml.core.config.InitializationService; +import org.opensaml.core.criterion.EntityIdCriterion; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.XMLObjectBuilder; +import org.opensaml.core.xml.config.XMLObjectProviderRegistry; +import org.opensaml.core.xml.io.*; +import org.opensaml.core.xml.schema.*; +import org.opensaml.saml.common.SAMLObject; +import org.opensaml.saml.common.SAMLVersion; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.criterion.EntityRoleCriterion; +import org.opensaml.saml.criterion.ProtocolCriterion; +import org.opensaml.saml.metadata.resolver.impl.HTTPMetadataResolver; +import org.opensaml.saml.metadata.resolver.impl.PredicateRoleDescriptorResolver; +import org.opensaml.saml.saml2.core.*; +import org.opensaml.saml.saml2.encryption.Decrypter; +import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; +import org.opensaml.saml.security.impl.MetadataCredentialResolver; +import org.opensaml.security.credential.Credential; +import org.opensaml.security.credential.CredentialSupport; +import org.opensaml.security.credential.UsageType; +import org.opensaml.security.criteria.UsageCriterion; +import org.opensaml.security.x509.BasicX509Credential; +import org.opensaml.security.x509.X509Credential; +import org.opensaml.security.x509.impl.KeyStoreX509CredentialAdapter; +import org.opensaml.soap.soap11.Body; +import org.opensaml.soap.soap11.Envelope; +import org.opensaml.xml.util.Base64; +import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap; +import org.opensaml.xmlsec.encryption.EncryptedKey; +import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver; +import org.opensaml.xmlsec.keyinfo.impl.StaticKeyInfoCredentialResolver; +import org.opensaml.xmlsec.signature.KeyInfo; +import org.opensaml.xmlsec.signature.Signature; +import org.opensaml.xmlsec.signature.X509Data; +import org.opensaml.xmlsec.signature.support.SignatureValidator; +import org.opensaml.xmlsec.signature.support.Signer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; +import org.xml.sax.SAXException; + +import javax.crypto.SecretKey; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.*; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +public class Saml2SSOUtils { + + private static final Logger logger = LoggerFactory.getLogger(Saml2SSOUtils.class); + private static boolean isBootStrapped = false; + private static BasicParserPool parserPool; + private static XMLObjectProviderRegistry registry; + + private Saml2SSOUtils() { + } + + private static void doBootstrap() throws Exception { + if (!isBootStrapped) { + try { + boostrap(); + isBootStrapped = true; + } catch (Exception e) { + throw new Exception("Error in bootstrapping the OpenSAML2 library", e); + } + } + } + + private static void boostrap(){ + parserPool = new BasicParserPool(); + parserPool.setMaxPoolSize(100); + parserPool.setCoalescing(true); + parserPool.setIgnoreComments(true); + parserPool.setIgnoreElementContentWhitespace(true); + parserPool.setNamespaceAware(true); + parserPool.setExpandEntityReferences(false); + parserPool.setXincludeAware(false); + + final Map features = new HashMap(); + features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE); + features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE); + features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE); + features.put("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE); + features.put("http://javax.xml.XMLConstants/feature/secure-processing", Boolean.TRUE); + + parserPool.setBuilderFeatures(features); + + parserPool.setBuilderAttributes(new HashMap()); + + try { + parserPool.initialize(); + } catch (ComponentInitializationException e) { + logger.error(e.getMessage(), e); + } + + registry = new XMLObjectProviderRegistry(); + ConfigurationService.register(XMLObjectProviderRegistry.class, registry); + registry.setParserPool(parserPool); + + try { + InitializationService.initialize(); + } catch (InitializationException e) { + logger.error(e.getMessage(), e); + } + } + + private static XMLObject buildXMLObject(QName objectQName) throws Exception { + + doBootstrap(); + XMLObjectBuilder builder = registry.getBuilderFactory().getBuilder(objectQName); + if (builder == null) { + throw new Exception("Unable to retrieve builder for object QName " + objectQName); + } + return builder.buildObject(objectQName.getNamespaceURI(), objectQName.getLocalPart(), objectQName.getPrefix()); + + } + + public static String getAttributeName(Attribute attribute, Saml2ConfigurableProvider.SAML2UsingFormat usingFormat){ + String friendlyName = attribute.getFriendlyName(); + String name = attribute.getName(); + if(usingFormat.getName().equals(Saml2ConfigurableProvider.SAML2UsingFormat.FRIENDLY_NAME.getName())){ + return (friendlyName != null) ? friendlyName : name; + } + else{ + return (name != null) ? name : friendlyName; + } + } + + public static Object getAttributeType(XMLObject attribute, Saml2ConfigurableProvider.SAML2AttributeType attributeType){ + + if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSSTRING.getType())){ + return ((XSString)attribute).getValue(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSINTEGER.getType())){ + return ((XSInteger)attribute).getValue(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSDATETIME.getType())){ + return ((XSDateTime)attribute).getValue(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSBOOLEAN.getType())){ + return ((XSBoolean)attribute).getValue(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSBASE64BINARY.getType())){ + return ((XSBase64Binary)attribute).getValue(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSURI.getType())){ + return ((XSURI)attribute).getURI(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSQNAME.getType())){ + return ((XSQName)attribute).getValue(); + } + else if(attributeType.getType().equals(Saml2ConfigurableProvider.SAML2AttributeType.XSANY.getType())){ + return ((XSAny)attribute).getTextContent(); + } + else { + return null; + } + + } + + private static String marshall(XMLObject xmlObject) throws Exception { + + try { + MarshallerFactory marshallerFactory = registry.getMarshallerFactory(); + Marshaller marshaller = marshallerFactory.getMarshaller(xmlObject); + Element element = marshaller.marshall(xmlObject); + + ByteArrayOutputStream byteArrayOutputStrm = new ByteArrayOutputStream(); + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS"); + LSSerializer writer = impl.createLSSerializer(); + LSOutput output = impl.createLSOutput(); + output.setByteStream(byteArrayOutputStrm); + writer.write(element, output); + return byteArrayOutputStrm.toString(); + } catch (Exception e) { + throw new Exception("Error Serializing the SAML Response", e); + } + } + + private static XMLObject unmarshall(String saml2SSOString) throws Exception { + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + //documentBuilderFactory.setExpandEntityReferences(false); + documentBuilderFactory.setNamespaceAware(true); + try { + DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder(); + ByteArrayInputStream is = new ByteArrayInputStream(saml2SSOString.getBytes(StandardCharsets.UTF_8)); + Document document = docBuilder.parse(is); + Element element = document.getDocumentElement(); + + UnmarshallerFactory unmarshallerFactory = registry.getUnmarshallerFactory(); + Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element); + return unmarshaller.unmarshall(element); + } catch (ParserConfigurationException | UnmarshallingException | SAXException | IOException e) { + throw new Exception("Error in unmarshalling SAML2SSO Request from the encoded String", e); + } + + } + + public static Assertion processArtifactResponse(String artifactString, Saml2ConfigurableProvider saml2Provider) throws Exception { + + doBootstrap(); + if (artifactString != null){ + ArtifactResolve artifactResolve = generateArtifactResolveReq(artifactString, saml2Provider); + ArtifactResponse artifactResponse = sendArtifactResolveRequest(artifactResolve, saml2Provider.getIdpArtifactUrl()); + Response saml2Response = (Response)artifactResponse.getMessage(); + return processSSOResponse(saml2Response, saml2Provider); + } + else { + throw new Exception("Invalid SAML2 Artifact. SAML2 Artifact can not be null."); + } + + } + + private static ArtifactResolve generateArtifactResolveReq(String samlArtReceived, Saml2ConfigurableProvider saml2Provider) throws Exception { + + ArtifactResolve artifactResolve = createArtifactResolveObject(samlArtReceived, saml2Provider.getSpEntityId()); + if (saml2Provider.isSignatureRequired()) { + signArtifactResolveReq(artifactResolve, saml2Provider); + } + return artifactResolve; + + } + + private static ArtifactResolve createArtifactResolveObject(String samlArtReceived, String spEntityId) throws Exception { + + ArtifactResolve artifactResolve = (ArtifactResolve)buildXMLObject(ArtifactResolve.DEFAULT_ELEMENT_NAME); + artifactResolve.setVersion(SAMLVersion.VERSION_20); + artifactResolve.setID(UUID.randomUUID().toString()); + artifactResolve.setIssueInstant(Instant.now()); + + Artifact artifact = (Artifact)buildXMLObject(Artifact.DEFAULT_ELEMENT_NAME); + artifact.setValue(samlArtReceived); + + Issuer issuer = (Issuer)buildXMLObject(Issuer.DEFAULT_ELEMENT_NAME); + issuer.setValue(spEntityId); + + artifactResolve.setIssuer(issuer); + artifactResolve.setArtifact(artifact); + + return artifactResolve; + + } + + private static void signArtifactResolveReq(ArtifactResolve artifactResolve, Saml2ConfigurableProvider saml2Provider) throws Exception { + + try { + KeyStore ks = KeyStore.getInstance("JKS"); + String archivePassword = saml2Provider.getSignatureKeyStorePassword(); + char[] pwdArray = (archivePassword != null) ? archivePassword.toCharArray() : "changeit".toCharArray(); + ks.load(new FileInputStream(saml2Provider.getSignaturePath()), pwdArray); + X509Credential cred = new KeyStoreX509CredentialAdapter(ks, saml2Provider.getSignatureKeyAlias(), saml2Provider.getSignatureKeyPassword().toCharArray()); + Signature signature = setSignatureRaw(XMLSignature.ALGO_ID_SIGNATURE_RSA, cred); + artifactResolve.setSignature(signature); + + List signatureList = new ArrayList<>(); + signatureList.add(signature); + + MarshallerFactory marshallerFactory = registry.getMarshallerFactory(); + Marshaller marshaller = marshallerFactory.getMarshaller(artifactResolve); + + marshaller.marshall(artifactResolve); + + org.apache.xml.security.Init.init(); + Signer.signObjects(signatureList); + } catch (Exception e) { + throw new Exception("Error while signing the SAML Request message", e); + } + } + + private static Signature setSignatureRaw(String signatureAlgorithm, X509Credential cred) throws Exception { + + Signature signature = (Signature)buildXMLObject(Signature.DEFAULT_ELEMENT_NAME); + signature.setSigningCredential(cred); + signature.setSignatureAlgorithm(signatureAlgorithm); + signature.setCanonicalizationAlgorithm(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); + + try { + KeyInfo keyInfo = (KeyInfo)buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME); + X509Data data = (X509Data)buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME); + org.opensaml.xmlsec.signature.X509Certificate cert = + (org.opensaml.xmlsec.signature.X509Certificate) buildXMLObject( + org.opensaml.xmlsec.signature.X509Certificate.DEFAULT_ELEMENT_NAME); + String value = org.apache.commons.codec.binary.Base64.encodeBase64String(cred.getEntityCertificate().getEncoded()); + cert.setValue(value); + data.getX509Certificates().add(cert); + keyInfo.getX509Datas().add(data); + signature.setKeyInfo(keyInfo); + return signature; + + } catch (CertificateEncodingException e) { + throw new Exception("Error getting certificate", e); + } + } + + private static ArtifactResponse sendArtifactResolveRequest(ArtifactResolve artifactResolve, String idpArtifactUrl) throws Exception { + + Envelope envelope = buildSOAPMessage(artifactResolve); + String envelopeElement; + try { + envelopeElement = marshall(envelope); + } catch (Exception e) { + throw new Exception("Encountered error marshalling SOAP message with artifact " + "resolve, into its DOM representation", e); + } + + String artifactResponseString = sendSOAP(envelopeElement, idpArtifactUrl); + + ArtifactResponse artifactResponse = extractArtifactResponse(artifactResponseString); + validateArtifactResponse(artifactResolve, artifactResponse); + return artifactResponse; + + } + + private static Envelope buildSOAPMessage(SAMLObject samlMessage) throws Exception { + + Envelope envelope = (Envelope)buildXMLObject(Envelope.DEFAULT_ELEMENT_NAME); + Body body = (Body)buildXMLObject(Body.DEFAULT_ELEMENT_NAME); + body.getUnknownXMLObjects().add(samlMessage); + envelope.setBody(body); + return envelope; + + } + + private static String sendSOAP(String message, String idpArtifactUrl) throws Exception { + + if (message == null) { + throw new Exception("Cannot send null SOAP message."); + } + if (idpArtifactUrl == null) { + throw new Exception("Cannot send SOAP message to null URL."); + } + + StringBuilder soapResponse = new StringBuilder(); + try { + HttpPost httpPost = new HttpPost(idpArtifactUrl); + setRequestProperties(idpArtifactUrl, message, httpPost); + HttpClient httpClient = getHttpClient(); + HttpResponse httpResponse = httpClient.execute(httpPost); + + int responseCode = httpResponse.getStatusLine().getStatusCode(); + if (responseCode != 200) { + throw new Exception("Problem in communicating with: " + idpArtifactUrl + ". Received response: " + responseCode); + } else { + soapResponse.append(getResponseBody(httpResponse)); + } + } catch (UnknownHostException e) { + throw new Exception("Unknown targeted host: " + idpArtifactUrl, e); + } catch (IOException e) { + throw new Exception("Could not open connection with host: " + idpArtifactUrl, e); + } + return soapResponse.toString(); + + } + + private static void setRequestProperties(String idpArtifactUrl, String message, HttpPost httpPost) { + + httpPost.addHeader("Content-Type", "text/xml; charset=utf-8"); + httpPost.addHeader("Accept", "text/xml; charset=utf-8"); + String sbSOAPAction = "\"" + idpArtifactUrl + "\""; + httpPost.addHeader("SOAPAction", sbSOAPAction); + httpPost.addHeader("Pragma", "no-cache"); + httpPost.addHeader("Cache-Control", "no-cache, no-store"); + + httpPost.setEntity(new StringEntity(message, ContentType.create("text/xml", StandardCharsets.UTF_8))); + + } + + private static HttpClient getHttpClient() throws Exception { + + CloseableHttpClient httpClient = null; + SSLContextBuilder builder = new SSLContextBuilder(); + try { + builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + builder.build()); + httpClient = HttpClients.custom().setSSLSocketFactory( + sslsf).build(); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + throw new Exception("Error while building trust store.", e); + } catch (KeyManagementException e) { + throw new Exception("Error while building socket factory.", e); + } + + return httpClient; + + } + + private static String getResponseBody(HttpResponse response) throws Exception { + + ResponseHandler responseHandler = new BasicResponseHandler(); + String responseBody; + try { + responseBody = responseHandler.handleResponse(response); + } catch (IOException e) { + throw new Exception("Error when retrieving the HTTP response body.", e); + } + return responseBody; + + } + + private static ArtifactResponse extractArtifactResponse(String artifactResponseString) throws Exception { + + ArtifactResponse artifactResponse = null; + InputStream stream = new ByteArrayInputStream(artifactResponseString.getBytes(StandardCharsets.UTF_8)); + try { + MessageFactory messageFactory = MessageFactory.newInstance(); + SOAPMessage soapMessage = messageFactory.createMessage(new MimeHeaders(), stream); + SOAPBody soapBody = soapMessage.getSOAPBody(); + Iterator iterator = soapBody.getChildElements(); + + while (iterator.hasNext()) { + SOAPBodyElement artifactResponseElement = (SOAPBodyElement) iterator.next(); + + if (StringUtils.equals(SAMLConstants.SAML20P_NS, artifactResponseElement.getNamespaceURI()) && + StringUtils.equals(ArtifactResponse.DEFAULT_ELEMENT_LOCAL_NAME, + artifactResponseElement.getLocalName())) { + + DOMSource source = new DOMSource(artifactResponseElement); + StringWriter stringResult = new StringWriter(); + TransformerFactory.newInstance().newTransformer().transform( + source, new StreamResult(stringResult)); + artifactResponse = (ArtifactResponse) unmarshall(stringResult.toString()); + } else { + throw new Exception("Received invalid artifact response with nameSpaceURI: " + + artifactResponseElement.getNamespaceURI() + " and localName: " + + artifactResponseElement.getLocalName()); + } + } + } catch (SOAPException | IOException | TransformerException e) { + throw new Exception("Didn't receive valid artifact response.", e); + } catch (Exception e) { + throw new Exception("Encountered error unmarshalling response into SAML2 object", e); + } + return artifactResponse; + + } + + private static void validateArtifactResponse(ArtifactResolve artifactResolve, ArtifactResponse artifactResponse) throws Exception { + + if (artifactResponse == null) { + throw new Exception("Received artifact response message was null."); + } + + String artifactResolveId = artifactResolve.getID(); + String artifactResponseInResponseTo = artifactResponse.getInResponseTo(); + if (!artifactResolveId.equals(artifactResponseInResponseTo)) { + throw new Exception("Artifact resolve ID: " + artifactResolveId + " is not equal to " + + "artifact response InResponseTo : " + artifactResponseInResponseTo); + } + + String artifactResponseStatus = artifactResponse.getStatus().getStatusCode().getValue(); + if (!StatusCode.SUCCESS.equals(artifactResponseStatus)) { + throw new Exception("Unsuccessful artifact response with status: " + + artifactResponseStatus); + } + + SAMLObject message = artifactResponse.getMessage(); + if (message == null) { + throw new Exception("No SAML response embedded into the artifact response."); + } + + } + + public static Assertion processResponse(String saml2SSOResponse, Saml2ConfigurableProvider saml2Provider) throws Exception { + + doBootstrap(); + if (saml2SSOResponse != null) { + byte[] decodedResponse = Base64.decode(saml2SSOResponse); + String response; + if(!saml2Provider.getBinding().equals("Post")){ + ByteArrayInputStream bytesIn = new ByteArrayInputStream(decodedResponse); + InflaterInputStream inflater = new InflaterInputStream(bytesIn, new Inflater(true)); + response = new BufferedReader(new InputStreamReader(inflater, StandardCharsets.UTF_8)) + .lines().collect(Collectors.joining("\n")); + } + else{ + response = new String(decodedResponse); + } + Response saml2Response = (Response) Saml2SSOUtils.unmarshall(response); + return processSSOResponse(saml2Response, saml2Provider); + + } else { + throw new Exception("Invalid SAML2 Response. SAML2 Response can not be null."); + } + } + + private static Assertion processSSOResponse(Response saml2Response, Saml2ConfigurableProvider saml2Provider) throws Exception { + + Assertion assertion = null; + if (saml2Provider.isAssertionEncrypted()) { + List encryptedAssertions = saml2Response.getEncryptedAssertions(); + EncryptedAssertion encryptedAssertion; + if (!CollectionUtils.isEmpty(encryptedAssertions)) { + encryptedAssertion = encryptedAssertions.get(0); + try { + assertion = getDecryptedAssertion(encryptedAssertion, saml2Provider); + } catch (Exception e) { + throw new Exception("Unable to decrypt the SAML2 Assertion"); + } + } + } else { + List assertions = saml2Response.getAssertions(); + if (assertions != null && !assertions.isEmpty()) { + assertion = assertions.get(0); + } + } + if (assertion == null) { + throw new Exception("SAML2 Assertion not found in the Response"); + } + + String idPEntityIdValue = assertion.getIssuer().getValue(); + if (idPEntityIdValue == null || idPEntityIdValue.isEmpty()) { + throw new Exception("SAML2 Response does not contain an Issuer value"); + } else if (!idPEntityIdValue.equals(saml2Provider.getIdpEntityId())) { + throw new Exception("SAML2 Response Issuer verification failed"); + } + + String subject = null; + if (assertion.getSubject() != null && assertion.getSubject().getNameID() != null) { + subject = assertion.getSubject().getNameID().getValue(); + } + + if (subject == null) { + throw new Exception("SAML2 Response does not contain the name of the subject"); + } + + validateAudienceRestriction(assertion, saml2Provider.getSpEntityId()); + + final HTTPMetadataResolver metadataResolver = new HTTPMetadataResolver(HttpClientBuilder.create().build(), saml2Provider.getIdpMetadataUrl()); + metadataResolver.setId(metadataResolver.getClass().getCanonicalName()); + metadataResolver.setParserPool(parserPool); + metadataResolver.initialize(); + + final MetadataCredentialResolver metadataCredentialResolver = new MetadataCredentialResolver(); + final PredicateRoleDescriptorResolver roleResolver = new PredicateRoleDescriptorResolver(metadataResolver); + final KeyInfoCredentialResolver keyResolver = DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver(); + metadataCredentialResolver.setKeyInfoCredentialResolver(keyResolver); + metadataCredentialResolver.setRoleDescriptorResolver(roleResolver); + metadataCredentialResolver.initialize(); + roleResolver.initialize(); + + CriteriaSet criteriaSet = new CriteriaSet(); + criteriaSet.add(new UsageCriterion(UsageType.SIGNING)); + criteriaSet.add(new EntityRoleCriterion(IDPSSODescriptor.DEFAULT_ELEMENT_NAME)); + criteriaSet.add(new ProtocolCriterion(SAMLConstants.SAML20P_NS)); + criteriaSet.add(new EntityIdCriterion(saml2Provider.getIdpEntityId())); + + Credential credential = metadataCredentialResolver.resolveSingle(criteriaSet); + + validateSignature(saml2Response, assertion, saml2Provider.isResponseSigned(), saml2Provider.isAssertionSigned(), credential); + + return assertion; + + } + + private static Assertion getDecryptedAssertion(EncryptedAssertion encryptedAssertion, Saml2ConfigurableProvider saml2Provider) throws Exception { + + try { + KeyStore ks = (saml2Provider.getKeyFormat().getType().equals("JKS")) ? KeyStore.getInstance("JKS") : KeyStore.getInstance("PKCS12"); + String archivePassword = saml2Provider.getArchivePassword(); + char[] pwdArray = (archivePassword != null) ? archivePassword.toCharArray() : "changeit".toCharArray(); + ks.load(new FileInputStream(saml2Provider.getCredentialPath()), pwdArray); + X509Certificate cert = (X509Certificate)ks.getCertificate(saml2Provider.getKeyAlias()); + PrivateKey pk = (PrivateKey) ks.getKey(saml2Provider.getKeyAlias(), saml2Provider.getKeyPassword().toCharArray()); + KeyInfoCredentialResolver keyResolver = new StaticKeyInfoCredentialResolver( + new BasicX509Credential(cert, pk)); + EncryptedKey key = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys().get(0); + Decrypter decrypter = new Decrypter(null, keyResolver, null); + SecretKey dkey = (SecretKey) decrypter.decryptKey(key, encryptedAssertion.getEncryptedData().getEncryptionMethod().getAlgorithm()); + Credential shared = CredentialSupport.getSimpleCredential(dkey); + decrypter = new Decrypter(new StaticKeyInfoCredentialResolver(shared), null, null); + decrypter.setRootInNewDocument(true); + return decrypter.decrypt(encryptedAssertion); + } catch (Exception e) { + throw new Exception("Decrypted assertion error", e); + + } + } + + private static void validateAudienceRestriction(Assertion assertion, String requiredSPEntityId) throws Exception { + + if (assertion != null) { + Conditions conditions = assertion.getConditions(); + if (conditions != null) { + List audienceRestrictions = conditions.getAudienceRestrictions(); + if (audienceRestrictions != null && !audienceRestrictions.isEmpty()) { + boolean audienceFound = false; + for (AudienceRestriction audienceRestriction : audienceRestrictions) { + if (audienceRestriction.getAudiences() != null && !audienceRestriction.getAudiences().isEmpty() + ) { + for (Audience audience : audienceRestriction.getAudiences()) { + if (requiredSPEntityId.equals(audience.getURI())) { + audienceFound = true; + break; + } + } + } + if (audienceFound) { + break; + } + } + if (!audienceFound) { + throw new Exception("SAML2 Assertion Audience Restriction validation failed"); + } + } else { + throw new Exception("SAML2 Response doesn't contain AudienceRestrictions"); + } + } else { + throw new Exception("SAML2 Response doesn't contain Conditions"); + } + } + } + + private static void validateSignature(Response response, Assertion assertion, Boolean isResponseSigned, Boolean isAssertionSigned, Credential credential) throws Exception { + + if (isResponseSigned) { + if (response.getSignature() == null) { + throw new Exception("SAML2 Response signing is enabled, but signature element not found in SAML2 Response element"); + } else { + try { + SignatureValidator.validate(response.getSignature(), credential); + } catch (Exception e) { + throw new Exception("Signature validation failed for SAML2 Response"); + } + } + } + if (isAssertionSigned) { + if (assertion.getSignature() == null) { + throw new Exception("SAML2 Assertion signing is enabled, but signature element not found in SAML2 Assertion element"); + } else { + try { + SignatureValidator.validate(assertion.getSignature(), credential); + } catch (Exception e) { + throw new Exception("Signature validation failed for SAML2 Assertion"); + } + } + } + } + +} \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java index df9549b8d..f1b1f8e83 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java @@ -59,12 +59,15 @@ public class WordBuilder { private CTAbstractNum cTAbstractNum; private BigInteger numId; private Integer indent; - private static final ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper; + private Integer imageCount; public WordBuilder(Environment environment) { this.cTAbstractNum = CTAbstractNum.Factory.newInstance(); this.cTAbstractNum.setAbstractNumId(BigInteger.valueOf(1)); this.indent = 0; + this.imageCount = 0; + this.mapper = new ObjectMapper(); this.buildOptions(environment); } @@ -157,6 +160,8 @@ public class WordBuilder { this.options.put(ParagraphStyle.IMAGE, (mainDocumentPart, item) -> { XWPFParagraph paragraph = mainDocumentPart.createParagraph(); paragraph.setPageBreak(true); + paragraph.setSpacingAfter(0); + paragraph.setAlignment(ParagraphAlignment.CENTER); //GK: Center the image if it is too small XWPFRun run = paragraph.createRun(); String imageId = ((Map)item).get("id"); String fileName = ((Map)item).get("name"); @@ -198,8 +203,16 @@ public class WordBuilder { width = Math.round(height/ratio); } - run.addPicture(image, format, fileName, Units.toEMU(width), Units.toEMU(height)); + XWPFPicture picture = run.addPicture(image, format, fileName, Units.toEMU(width), Units.toEMU(height)); paragraph.setPageBreak(false); + imageCount++; + XWPFParagraph captionParagraph = mainDocumentPart.createParagraph(); + captionParagraph.setAlignment(ParagraphAlignment.CENTER); + captionParagraph.setSpacingBefore(0); + captionParagraph.setStyle("Caption"); + XWPFRun captionRun = captionParagraph.createRun(); + captionRun.setText("Image " + imageCount); + } } catch (IOException | InvalidFormatException e){ logger.error(e.getMessage(), e); @@ -320,7 +333,7 @@ public class WordBuilder { return false; }).collect(Collectors.toList()); for (Field field: tempFields) { - if (visibilityRuleService.isElementVisible(field.getId())) { + if (visibilityRuleService.isElementVisible(field.getId()) && field.getExport()) { if (!createListing) { try { if(field.getViewStyle().getRenderStyle().equals("upload")){ @@ -548,7 +561,7 @@ public class WordBuilder { } private boolean hasVisibleFields(FieldSet compositeFields, VisibilityRuleService visibilityRuleService) { - return compositeFields.getFields().stream().anyMatch(field -> visibilityRuleService.isElementVisible(field.getId())); + return compositeFields.getFields().stream().anyMatch(field -> visibilityRuleService.isElementVisible(field.getId()) && field.getExport()); } private Map customParse(String value) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/components/datasetprofile/Field.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/components/datasetprofile/Field.java index 8847c8692..f9c29e236 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/components/datasetprofile/Field.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/components/datasetprofile/Field.java @@ -54,6 +54,7 @@ public class Field implements ViewStyleDefinition validations; + private Boolean export; public String getId() { return id; @@ -132,6 +133,14 @@ public class Field implements ViewStyleDefinition validations; + private Boolean export; + public String getId() { return id; } @@ -89,6 +91,14 @@ public class Field implements DatabaseViewStyleDefinition, XmlSerializable getMultiplicityItems() { return multiplicityItems; } @@ -160,6 +162,14 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin this.rdaProperty = rdaProperty; } + public Boolean getExport() { + return export; + } + + public void setExport(Boolean export) { + this.export = export; + } + Field cloneForMultiplicity(String key, Map properties, int index) { Field newField = new Field(); newField.id = key; @@ -173,6 +183,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin newField.validations = this.validations; newField.rdaProperty = this.rdaProperty; newField.numbering = "mult" + index + "_" + this.numbering; + newField.export = this.export; return newField; } @@ -186,6 +197,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin field.setVisible(this.visible); field.setValidations(this.validations); field.setRdaCommonStandard(this.rdaProperty); + field.setExport(this.export); return field; } @@ -200,6 +212,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin this.visible = item.getVisible(); this.validations = item.getValidations(); this.rdaProperty = item.getRdaCommonStandard(); + this.export = item.getExport(); } @Override diff --git a/dmp-backend/web/src/main/resources/configurableLoginProviders.json b/dmp-backend/web/src/main/resources/configurableLoginProviders.json index ae519164b..b3d8114b2 100644 --- a/dmp-backend/web/src/main/resources/configurableLoginProviders.json +++ b/dmp-backend/web/src/main/resources/configurableLoginProviders.json @@ -2,7 +2,8 @@ "providers": [ { "enabled": false, - "configurableLoginId": "myId", + "type": "oauth2", + "configurableLoginId": "oauth2-localhost", "name": "myApp", "clientId": "", "clientSecret": "", @@ -21,7 +22,32 @@ }, "oauthUrl": "/authorize", "scope": "email", - "state": "123562" + "state": "123562", + "logoUrl": null + }, + { + "enabled": false, + "type": "", + "configurableLoginId": "", + "name": "", + "spEntityId": "", + "idpEntityId": "", + "idpUrl": "", + "idpArtifactUrl": "", + "idpMetadataUrl": "", + "assertionEncrypted": null, + "keyFormat": "", + "keyAlias": "", + "credentialPath": "", + "archivePassword": "", + "keyPassword": "", + "responseSigned": null, + "assertionSigned": null, + "usingFormat": "", + "configurableUserFromAttributes": null, + "attributeTypes": null, + "binding": "", + "logoUrl": "" } ] } diff --git a/dmp-backend/web/src/main/resources/documents/h2020.docx b/dmp-backend/web/src/main/resources/documents/h2020.docx index 0e61887c4..be91c70f9 100644 Binary files a/dmp-backend/web/src/main/resources/documents/h2020.docx and b/dmp-backend/web/src/main/resources/documents/h2020.docx differ diff --git a/dmp-frontend/package.json b/dmp-frontend/package.json index 12b85564e..3838fb831 100644 --- a/dmp-frontend/package.json +++ b/dmp-frontend/package.json @@ -38,6 +38,7 @@ "ngx-dropzone": "^3.0.0", "ngx-guided-tour": "^1.1.11", "ngx-matomo": "^0.1.4", + "pako": "^1.0.11", "rxjs": "^6.3.2", "tinymce": "^5.9.2", "tslib": "^2.0.0", @@ -47,12 +48,12 @@ "devDependencies": { "@angular-devkit/build-angular": "~12.2.7", "@angular/cdk": "^12.2.7", - "@angular/material": "^12.2.7", "@angular/cli": "12.2.7", "@angular/compiler-cli": "^12.2.7", + "@angular/language-service": "^12.2.7", + "@angular/material": "^12.2.7", "@angular/platform-browser-dynamic": "^12.2.7", "@angular/router": "^12.2.7", - "@angular/language-service": "^12.2.7", "@types/facebook-js-sdk": "^3.3.5", "@types/file-saver": "^2.0.3", "@types/gapi": "^0.0.41", @@ -61,6 +62,7 @@ "@types/jasminewd2": "~2.0.10", "@types/moment-timezone": "^0.5.13", "@types/node": "^12.11.1", + "@types/pako": "^1.0.3", "codelyzer": "^6.0.2", "ts-node": "~10.2.1", "tslint": "~6.1.0", diff --git a/dmp-frontend/src/app/core/common/enum/configurable-provider-type.ts b/dmp-frontend/src/app/core/common/enum/configurable-provider-type.ts new file mode 100644 index 000000000..8f1a01e8b --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/configurable-provider-type.ts @@ -0,0 +1,4 @@ +export enum ConfigurableProviderType { + Oauth2 = "oauth2", + Saml2 = "saml2" +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts b/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts index 859e79356..02c91c68d 100644 --- a/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts +++ b/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts @@ -59,6 +59,7 @@ export interface Field { visible: Visibility; validations: ValidationType[]; rdaCommonStandard: string; + export: boolean; } export interface ViewStyle { diff --git a/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts b/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts index c8098f618..d69525c1c 100644 --- a/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts +++ b/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts @@ -1,9 +1,6 @@ export class ConfigurableProvider { configurableLoginId: string; + type: string; name: string; - clientId: string; - redirect_uri: string; - oauthUrl: string; - scope: string; - state: string; + logoUrl: string; } diff --git a/dmp-frontend/src/app/core/model/configurable-provider/oauth2ConfigurableProvider.ts b/dmp-frontend/src/app/core/model/configurable-provider/oauth2ConfigurableProvider.ts new file mode 100644 index 000000000..7080cbccc --- /dev/null +++ b/dmp-frontend/src/app/core/model/configurable-provider/oauth2ConfigurableProvider.ts @@ -0,0 +1,9 @@ +import { ConfigurableProvider } from "./configurableProvider"; + +export class Oauth2ConfigurableProvider extends ConfigurableProvider{ + clientId: string; + redirect_uri: string; + oauthUrl: string; + scope: string; + state: string; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/configurable-provider/saml2ConfigurableProvider.ts b/dmp-frontend/src/app/core/model/configurable-provider/saml2ConfigurableProvider.ts new file mode 100644 index 000000000..997e62466 --- /dev/null +++ b/dmp-frontend/src/app/core/model/configurable-provider/saml2ConfigurableProvider.ts @@ -0,0 +1,8 @@ +import { ConfigurableProvider } from "./configurableProvider"; + +export class Saml2ConfigurableProvider extends ConfigurableProvider{ + spEntityId: string; + idpUrl: string; + binding: string; + assertionConsumerServiceUrl: string; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/auth/auth.service.ts b/dmp-frontend/src/app/core/services/auth/auth.service.ts index e3fb7875e..b921df883 100644 --- a/dmp-frontend/src/app/core/services/auth/auth.service.ts +++ b/dmp-frontend/src/app/core/services/auth/auth.service.ts @@ -181,4 +181,23 @@ export class AuthService extends BaseService { }) ); } + + public getUserFromToken(token: string): Observable { + this.actionUrl = this.configurationService.server + 'auth/'; + const url = this.actionUrl + 'me'; + let headers = this.headers; + headers = headers.set('AuthToken', token); + return this.http.post(url, null, { headers: headers }).pipe( + map((res: any) => { + const princ = this.current(res.payload); + princ.expiresAt = new Date(princ.expiresAt); + return princ; + }), + catchError((error: any) => { + this.clear(); + const princ = this.current(); + this.router.navigate(['/login']); + return observableOf(princ); + })); + } } diff --git a/dmp-frontend/src/app/core/services/saml-login.service.ts b/dmp-frontend/src/app/core/services/saml-login.service.ts new file mode 100644 index 000000000..b0802de76 --- /dev/null +++ b/dmp-frontend/src/app/core/services/saml-login.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; +import { Guid } from '@common/types/guid'; +import * as pk from 'pako'; + +@Injectable() +export class SamlLoginService { + + constructor() {} + + buildRelayState(spId: string, configurableLoginId: string): string { + let uri = 'spId=' + spId; + uri += '&configurableLoginId=' + configurableLoginId; + return encodeURIComponent(uri); + } + + resolveConfigurableLoginId(relayState: string): string { + const decoded = decodeURIComponent(relayState); + const routeParams = new URLSearchParams(decoded); + return routeParams.has('configurableLoginId') ? routeParams.get('configurableLoginId') : undefined; + } + resolveSpId(relayState: string): string { + const decoded = decodeURIComponent(relayState); + const routeParams = new URLSearchParams(decoded); + return routeParams.has('spId') ? routeParams.get('spId') : ''; + } + + getSamlLoginUrl(spEntityID: string, idpUrl: string, binding: string, assertionConsumerServiceUrl: string, configurableLoginId: string) { + const now = new Date(); + let protocolBinding = ''; + switch (binding) { + case "Redirect": protocolBinding = 'ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" '; break; + case "Artifact": protocolBinding = 'ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" '; break; + case "Post": protocolBinding = 'ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" '; break; + } + const authenticationRequest = '' + + '' + spEntityID + '' + + ''; + const uint = new Uint8Array(authenticationRequest.length); + for (let i = 0, j = authenticationRequest.length; i < j; ++i) { + uint[i] = authenticationRequest.charCodeAt(i); + } + const base64String = btoa(pk.deflateRaw(uint, { to: 'string' })); + const relayState = this.buildRelayState(spEntityID, configurableLoginId); + const url = idpUrl + '?RelayState=' + relayState + '&SAMLRequest=' + encodeURIComponent(base64String); + return url; + } + +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts index 741a02160..59afcd724 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts @@ -48,6 +48,7 @@ export class FieldEditorModel extends BaseFormModel { public data: FieldDataEditorModel; public validations: ValidationType[] = []; public rdaCommonStandard: string; + public export: boolean = true; fromModel(item: Field): FieldEditorModel { this.id = item.id; @@ -58,6 +59,7 @@ export class FieldEditorModel extends BaseFormModel { this.viewStyle = new ViewStyleEditorModel().fromModel(item.viewStyle); this.visible = new VisibilityEditorModel().fromModel(item.visible); this.rdaCommonStandard = item.rdaCommonStandard; + this.export = item.export; if (item.data) { if (this.viewStyle.renderStyle === 'combobox') { @@ -103,7 +105,8 @@ export class FieldEditorModel extends BaseFormModel { page: [{ value: this.page, disabled: (disabled && !skipDisable.includes('FieldEditorModel.page')) }], ordinal: [{ value: this.ordinal, disabled: (disabled && !skipDisable.includes('FieldEditorModel.ordinal')) }], validations: [{ value: this.validations, disabled: (disabled && !skipDisable.includes('FieldEditorModel.validations')) }], - rdaCommonStandard: [{value: this.rdaCommonStandard, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.rdaCommonStandard')) }] + rdaCommonStandard: [{value: this.rdaCommonStandard, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.rdaCommonStandard')) }], + export: [{value: this.export, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.export'))}] }); formGroup.addControl('defaultValue', this.defaultValue.buildForm(disabled, skipDisable)); diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts index a876a0269..bc404e5e4 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts @@ -535,7 +535,8 @@ export class DatasetProfileEditorCompositeFieldComponent extends BaseComponent i ordinal: targetOrdinal, visible:{rules:[],style:null}, validations:[], - viewStyle:{} + viewStyle:{}, + export: true } as Field; diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html index 51e8282fe..ffed9ee2f 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html @@ -236,6 +236,8 @@ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.EXPORT' | translate}} + this.selectedEntryId.emit(id)) // ); - + } ngOnDestroy(){ // console.log('elemement destroyed -->'); - + this.subs.unsubscribe(); } ngOnChanges(changes: SimpleChanges): void { @@ -129,7 +129,7 @@ export class DatasetProfileEditorSectionFieldSetComponent implements OnInit, OnC this.selectedEntryId.emit(id); } - + @@ -184,7 +184,7 @@ export class DatasetProfileEditorSectionFieldSetComponent implements OnInit, OnC } } ngOnInit() { - + } onRemoveFieldSet(fieldsetId: string){ @@ -252,7 +252,7 @@ export class DatasetProfileEditorSectionFieldSetComponent implements OnInit, OnC if(tocEntryFound){ return tocEntryFound; - } + } for(let entry of tocentries){ const result = this._findTocEntryById(id, entry.subEntries); @@ -261,7 +261,7 @@ export class DatasetProfileEditorSectionFieldSetComponent implements OnInit, OnC break; } } - + return tocEntryFound? tocEntryFound: null; } } diff --git a/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.ts b/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.ts index 69d35a0d4..e0ed91f84 100644 --- a/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.ts +++ b/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.ts @@ -10,6 +10,10 @@ import { BaseComponent } from '@common/base/base.component'; import { environment } from 'environments/environment'; import { takeUntil } from 'rxjs/operators'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { SamlLoginService } from '@app/core/services/saml-login.service'; +import { Oauth2ConfigurableProvider } from '@app/core/model/configurable-provider/oauth2ConfigurableProvider'; +import { Saml2ConfigurableProvider } from '@app/core/model/configurable-provider/saml2ConfigurableProvider'; +import { ConfigurableProviderType } from '@app/core/common/enum/configurable-provider-type'; @Component({ selector: 'app-configurable-login', @@ -35,7 +39,8 @@ export class ConfigurableLoginComponent extends BaseComponent implements OnInit private router: Router, private httpClient: HttpClient, private providers: ConfigurableProvidersService, - private configurationService: ConfigurationService + private configurationService: ConfigurationService, + private samlLoginService: SamlLoginService ) { super(); } @@ -70,16 +75,23 @@ export class ConfigurableLoginComponent extends BaseComponent implements OnInit } public configurableAuthorize() { - let authUrl = this.provider.oauthUrl - + '?response_type=code&client_id=' + this.provider.clientId - + '&redirect_uri=' + this.provider.redirect_uri - + '&scope=' + this.provider.scope; - if (this.provider.state.length > 0) authUrl = authUrl + '&state=' + this.provider.state - window.location.href = authUrl; + if(this.provider.type === ConfigurableProviderType.Oauth2){ + let provider = this.provider as Oauth2ConfigurableProvider; + let authUrl = provider.oauthUrl + + '?response_type=code&client_id=' + provider.clientId + + '&redirect_uri=' + provider.redirect_uri + + '&scope=' + provider.scope; + if (provider.state.length > 0) authUrl = authUrl + '&state=' + provider.state + window.location.href = authUrl; + } + else if(this.provider.type === ConfigurableProviderType.Saml2){ + let provider = this.provider as Saml2ConfigurableProvider; + window.location.href = this.samlLoginService.getSamlLoginUrl(provider.spEntityId, provider.idpUrl, provider.binding, provider.assertionConsumerServiceUrl, provider.configurableLoginId); + } } public configurableLoginUser(code: string, state: string) { - if (state !== this.provider.state) { + if (state !== (this.provider).state) { this.router.navigate(['/login']) } this.httpClient.post(this.configurationService.server + 'auth/configurableProviderRequestToken', { code: code, provider: AuthProvider.Configurable, configurableLoginId: this.providerId }) diff --git a/dmp-frontend/src/app/ui/auth/login/login.component.html b/dmp-frontend/src/app/ui/auth/login/login.component.html index 13ef44581..bfb4b9f13 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.component.html +++ b/dmp-frontend/src/app/ui/auth/login/login.component.html @@ -50,17 +50,24 @@ -
-
-