Merge branch 'saml2' into Development

This commit is contained in:
Diamantis Tziotzios 2022-06-01 10:45:19 +03:00
commit 91566c9710
32 changed files with 1664 additions and 175 deletions

View File

@ -120,6 +120,66 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<version>${opensaml.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
<version>${opensaml.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-impl</artifactId>
<version>${opensaml.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-soap-api</artifactId>
<version>${opensaml.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-xmlsec-api</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-security-api</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-security-impl</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-profile-api</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>xmltooling</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>jakarta.xml.soap</groupId>
<artifactId>jakarta.xml.soap-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>3.0.0-M2</version>
</dependency>
</dependencies>
<build>
@ -155,5 +215,6 @@
<properties>
<start-class>eu.eudat.EuDatApplication</start-class>
<opensaml.version>4.0.1</opensaml.version>
</properties>
</project>

View File

@ -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<Void> verify(@RequestParam(value = "SAMLResponse") String SAMLResponse, @RequestParam(value = "RelayState") String RelayState) throws GeneralSecurityException {
Map<String, String> 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<String, String> 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();
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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<String> entity = new HttpEntity<>(headers);

View File

@ -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())

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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<String, SAML2AttributeType> attributeTypes;
private Map<String, String> 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<String, String> getConfigurableUserFromAttributes() {
return configurableUserFromAttributes;
}
public void setConfigurableUserFromAttributes(Map<String, String> configurableUserFromAttributes) {
this.configurableUserFromAttributes = configurableUserFromAttributes;
}
public Map<String, SAML2AttributeType> getAttributeTypes() {
return attributeTypes;
}
public void setAttributeTypes(Map<String, SAML2AttributeType> 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;
}
}

View File

@ -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;
}
}

View File

@ -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,10 +25,16 @@ public class ConfigurableProvidersModel {
List<ConfigurableProviderModel> providerModelList = new LinkedList<>();
if (entity != null) {
for (ConfigurableProvider entityProvider : entity.getProviders()) {
if (entityProvider.getEnabled())
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);
return model;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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();
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<AttributeStatement> attributeStatements = saml2Assertion.getAttributeStatements();
if(attributeStatements != null && !attributeStatements.isEmpty()){
List<Attribute> attributes = attributeStatements.get(0).getAttributes();
if(attributes != null && !attributes.isEmpty()){
Saml2ConfigurableProvider.SAML2UsingFormat usingFormat = ((Saml2ConfigurableProvider)configurableProvider).getUsingFormat();
Map<String, String> attributeMapping = ((Saml2ConfigurableProvider)configurableProvider).getConfigurableUserFromAttributes();
Map<String, Saml2ConfigurableProvider.SAML2AttributeType> attributeType = ((Saml2ConfigurableProvider)configurableProvider).getAttributeTypes();
Map<String, Object> 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) {

View File

@ -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<String, Boolean> features = new HashMap<String, Boolean>();
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<String, Object>());
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<Signature> 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<String> 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<Node> 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<EncryptedAssertion> 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<Assertion> 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<AudienceRestriction> 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");
}
}
}
}
}

View File

@ -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": ""
}
]
}

View File

@ -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",

View File

@ -0,0 +1,4 @@
export enum ConfigurableProviderType {
Oauth2 = "oauth2",
Saml2 = "saml2"
}

View File

@ -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;
}

View File

@ -0,0 +1,9 @@
import { ConfigurableProvider } from "./configurableProvider";
export class Oauth2ConfigurableProvider extends ConfigurableProvider{
clientId: string;
redirect_uri: string;
oauthUrl: string;
scope: string;
state: string;
}

View File

@ -0,0 +1,8 @@
import { ConfigurableProvider } from "./configurableProvider";
export class Saml2ConfigurableProvider extends ConfigurableProvider{
spEntityId: string;
idpUrl: string;
binding: string;
assertionConsumerServiceUrl: string;
}

View File

@ -181,4 +181,23 @@ export class AuthService extends BaseService {
})
);
}
public getUserFromToken(token: string): Observable<Principal> {
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<Principal>(princ);
}));
}
}

View File

@ -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 = '<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_' + Guid.create() + '" Version="2.0" ' +
'IssueInstant="' + now.toISOString() + '" ' +
protocolBinding +
'AssertionConsumerServiceUrl="' + assertionConsumerServiceUrl + '" ' +
'Destination="' + idpUrl + '">' +
'<saml2:Issuer>' + spEntityID + '</saml2:Issuer>' +
'</saml2p:AuthnRequest>';
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;
}
}

View File

@ -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
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 !== (<Oauth2ConfigurableProvider>this.provider).state) {
this.router.navigate(['/login'])
}
this.httpClient.post(this.configurationService.server + 'auth/configurableProviderRequestToken', { code: code, provider: AuthProvider.Configurable, configurableLoginId: this.providerId })

View File

@ -50,17 +50,24 @@
</div>
</div>
</div>
<div class="row justify-content-center">
<div *ngIf="hasConfigurableProviders()" class="row pt-2 mb-4 accesss-methods">
<div *ngFor="let provider of this.configurableProviderService.providers"
class="col-auto configurable-logo">
<button mat-icon-button class="configurable-button" (click)="configurableLogin(provider)"
class="login-social-button">
<div class="row justify-content-center pt-4">
<ng-template [ngIf]="hasConfigurableProviders()">
<div *ngFor="let provider of this.configurableProviderService.providers; index as i" class="flex-column">
<div class="col-auto"
[ngClass]="{'pr-4': (i % this.configurableProviderService.providers.length) == 0 || (i % this.configurableProviderService.providers.length) == 1 ||
this.configurableProviderService.providers.length == 1 || this.configurableProviderService.providers.length == 2,
'pl-4': (i % this.configurableProviderService.providers.length) == 2 || (i % this.configurableProviderService.providers.length) == 1 ||
this.configurableProviderService.providers.length == 1 || this.configurableProviderService.providers.length == 2}">
<button mat-icon-button class="iconmediumButton d-flex justify-content-center align-items-center" (click)="configurableLogin(provider)">
<span *ngIf="provider.logoUrl; else elseBlock" class="configurableIcon" style="background: url({{provider.logoUrl}}) no-repeat; background-size: cover;"></span>
<ng-template #elseBlock>
<span class="configurableIcon">{{provider.name}}</span>
</ng-template>
</button>
</div>
</div>
<div *ngIf="hasZenodoOauth()" class="col-auto mt-4">
</ng-template>
<div *ngIf="hasZenodoOauth()" class="col-auto">
<button mat-icon-button (click)="zenodoLogin()" class="d-flex justify-content-center">
<span class="zenodoIcon"></span>
</button>

View File

@ -294,8 +294,9 @@ span.iconmedium {
span.configurableIcon {
float: right;
width: 80px;
height: 56px;
transform: scale(0.85);
width: 50px;
height: 50px;
}
span.zenodoIcon {

View File

@ -16,6 +16,8 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
import { MergeLoginService } from './utilities/merge-login.service';
import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
import { SamlLoginService } from '@app/core/services/saml-login.service';
import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.component';
@NgModule({
imports: [
@ -34,11 +36,12 @@ import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
OpenAireLoginComponent,
ConfigurableLoginComponent,
ZenodoLoginComponent,
MergeEmailConfirmation
MergeEmailConfirmation,
SamlResponseLoginComponent
],
exports: [
LoginComponent
],
providers: [LoginService, MergeLoginService, ConfigurableProvidersService]
providers: [LoginService, MergeLoginService, ConfigurableProvidersService, SamlLoginService]
})
export class LoginModule { }

View File

@ -11,6 +11,7 @@ import { ConfigurableLoginComponent } from "./configurable-login/configurable-lo
import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component';
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.component';
const routes: Routes = [
{ path: '', component: LoginComponent },
@ -23,7 +24,8 @@ const routes: Routes = [
{ path: 'confirmation', component: EmailConfirmation },
{ path: 'openaire', component: Oauth2DialogComponent},
{ path: 'configurable/:id', component: ConfigurableLoginComponent},
{ path: 'external/zenodo', component: Oauth2DialogComponent }
{ path: 'external/zenodo', component: Oauth2DialogComponent },
{ path: 'external/saml', component: SamlResponseLoginComponent }
];
@NgModule({

View File

@ -0,0 +1,68 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, NgZone, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BaseComponent } from '@common/base/base.component';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { SamlLoginService } from '@app/core/services/saml-login.service';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { AuthService } from '@app/core/services/auth/auth.service';
import { AuthProvider } from '@app/core/common/enum/auth-provider';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
@Component({
template: ''
})
export class SamlResponseLoginComponent extends BaseComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private samlLoginService: SamlLoginService,
private router: Router,
private uiNotificationService: UiNotificationService,
private loggingService: LoggingService,
private zone: NgZone,
private language: TranslateService,
private authService: AuthService,
) { super(); }
ngOnInit() {
this.route.queryParams
.pipe(takeUntil(this._destroyed))
.subscribe(routeParams => {
let samlResponse = null;
if (routeParams.SAMLart) {
samlResponse = routeParams.SAMLart;
} else if (routeParams.SAMLResponse) {
samlResponse = routeParams.SAMLResponse;
}
else if(routeParams.token){
this.authService.getUserFromToken(routeParams.token).pipe(takeUntil(this._destroyed))
.subscribe((result) => this.onAuthenticateSuccess(), (error) => this.onAuthenticateError(error));
return;
}
if (samlResponse == null) this.router.navigate(['/login']);
const spId = this.samlLoginService.resolveSpId(routeParams.RelayState);
const configurableLoginId = this.samlLoginService.resolveConfigurableLoginId(routeParams.RelayState);
this.authService.login({ ticket: samlResponse, provider: AuthProvider.Configurable, data: { configurableLoginId: configurableLoginId } })
.pipe(takeUntil(this._destroyed))
.subscribe((result) => this.onAuthenticateSuccess(), (error) => this.onAuthenticateError(error));
});
}
onAuthenticateSuccess(): void {
this.loggingService.info('Successful Login');
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGIN'), SnackBarNotificationLevel.Success);
this.zone.run(() => this.router.navigate(['/']));
}
onAuthenticateError(errorResponse: HttpErrorResponse) {
this.uiNotificationService.snackBarNotification(errorResponse.error.message, SnackBarNotificationLevel.Warning);
this.zone.run(() => this.router.navigate(['/']));
}
}