diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java index 53950a5a2..954ab00e2 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java @@ -3,10 +3,15 @@ package eu.eudat.controllers; import eu.eudat.core.logger.Logger; import eu.eudat.exceptions.security.NullEmailException; import eu.eudat.logic.managers.UserManager; +import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.security.CustomAuthenticationProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.models.ConfigurableProvidersModel; import eu.eudat.logic.security.validators.b2access.B2AccessTokenValidator; import eu.eudat.logic.security.validators.b2access.helpers.B2AccessRequest; import eu.eudat.logic.security.validators.b2access.helpers.B2AccessResponseToken; +import eu.eudat.logic.security.validators.configurableProvider.ConfigurableProviderTokenValidator; +import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderRequest; +import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderResponseToken; import eu.eudat.logic.security.validators.linkedin.LinkedInTokenValidator; import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInRequest; import eu.eudat.logic.security.validators.linkedin.helpers.LinkedInResponseToken; @@ -45,6 +50,8 @@ public class Login { private ORCIDTokenValidator orcidTokenValidator; private LinkedInTokenValidator linkedInTokenValidator; private OpenAIRETokenValidator openAIRETokenValidator; + private ConfigurableProviderTokenValidator configurableProviderTokenValidator; + private ConfigLoader configLoader; private Logger logger; @@ -52,7 +59,7 @@ public class Login { @Autowired public Login(CustomAuthenticationProvider customAuthenticationProvider, AuthenticationService nonVerifiedUserAuthenticationService, TwitterTokenValidator twitterTokenValidator, LinkedInTokenValidator linkedInTokenValidator, B2AccessTokenValidator b2AccessTokenValidator, - ORCIDTokenValidator orcidTokenValidator, OpenAIRETokenValidator openAIRETokenValidator, UserManager userManager, Logger logger) { + ORCIDTokenValidator orcidTokenValidator, OpenAIRETokenValidator openAIRETokenValidator, ConfigurableProviderTokenValidator configurableProviderTokenValidator, ConfigLoader configLoader, UserManager userManager, Logger logger) { this.customAuthenticationProvider = customAuthenticationProvider; this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; this.twitterTokenValidator = twitterTokenValidator; @@ -60,6 +67,8 @@ public class Login { this.b2AccessTokenValidator = b2AccessTokenValidator; this.orcidTokenValidator = orcidTokenValidator; this.openAIRETokenValidator = openAIRETokenValidator; + this.configurableProviderTokenValidator = configurableProviderTokenValidator; + this.configLoader = configLoader; this.logger = logger; this.userManager = userManager; } @@ -110,6 +119,12 @@ public class Login { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(this.openAIRETokenValidator.getAccessToken(openAIRERequest)).status(ApiMessageCode.NO_MESSAGE)); } + @RequestMapping(method = RequestMethod.POST, value = {"/configurableProviderRequestToken"}, produces = "application/json", consumes = "application/json") + public @ResponseBody + ResponseEntity> configurableProviderRequestToken(@RequestBody ConfigurableProviderRequest configurableProviderRequest) { + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(this.configurableProviderTokenValidator.getAccessToken(configurableProviderRequest)).status(ApiMessageCode.NO_MESSAGE)); + } + @RequestMapping(method = RequestMethod.POST, value = {"/me"}, consumes = "application/json", produces = "application/json") public @ResponseBody ResponseEntity> authMe(Principal principal) throws NullEmailException { @@ -125,4 +140,10 @@ public class Login { this.logger.info(principal, "Logged Out"); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); } + + @RequestMapping(method = RequestMethod.GET, value = {"/configurableLogin"}, consumes = "application/json", produces = "application/json") + public @ResponseBody + ResponseEntity> getConfigurableProviders() { + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(new ConfigurableProvidersModel().fromDataModel(configLoader.getConfigurableProviders())).status(ApiMessageCode.NO_MESSAGE)); + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java index 564568ad5..bf6910247 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java @@ -3,7 +3,6 @@ package eu.eudat.logic.managers; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.dao.entities.UserInfoDao; import eu.eudat.data.entities.DMP; -import eu.eudat.data.entities.UserDMP; import eu.eudat.data.entities.UserInfo; import eu.eudat.data.entities.UserRole; import eu.eudat.data.query.items.table.userinfo.UserInfoTableRequestItem; diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ConfigLoader.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ConfigLoader.java index 3b3365c2b..8a935aa53 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ConfigLoader.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ConfigLoader.java @@ -1,6 +1,7 @@ package eu.eudat.logic.proxy.config.configloaders; import eu.eudat.logic.proxy.config.ExternalUrls; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviders; import org.apache.poi.xwpf.usermodel.XWPFDocument; import java.util.List; @@ -9,4 +10,5 @@ public interface ConfigLoader { ExternalUrls getExternalUrls(); List getRdaProperties(); XWPFDocument getDocument(); + ConfigurableProviders getConfigurableProviders(); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java index f973eee99..934a472c4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java @@ -1,6 +1,9 @@ package eu.eudat.logic.proxy.config.configloaders; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.logic.proxy.config.ExternalUrls; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviders; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; @@ -24,6 +27,7 @@ public class DevelConfigLoader implements ConfigLoader { private ExternalUrls externalUrls; private List rdaProperties; private XWPFDocument document; + private ConfigurableProviders configurableProviders; @Autowired private Environment environment; @@ -40,7 +44,6 @@ public class DevelConfigLoader implements ConfigLoader { Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); is = new URL("file:///" + current + fileUrl).openStream(); externalUrls = (ExternalUrls) jaxbUnmarshaller.unmarshal(is); - } catch (Exception ex) { ex.printStackTrace(); System.out.println("Cannot find in folder" + current); @@ -77,12 +80,39 @@ public class DevelConfigLoader implements ConfigLoader { private void setDocument() { String filePath = environment.getProperty("configuration.h2020template"); String current = null; + InputStream is = null; try { current = new java.io.File(".").getCanonicalPath(); - InputStream is = new URL("file:///" + current + filePath).openStream(); + is = new URL("file:///" + current + filePath).openStream(); this.document = new XWPFDocument(is); } catch (IOException e) { e.printStackTrace(); + } finally { + try { + if (is != null) is.close(); + } catch (IOException e) { + System.out.println("Warning: Could not close a stream after reading from file: " + filePath); + } + } + } + + public void setConfigurableProviders() { + String filePath = environment.getProperty("configuration.configurable_login_providers"); + String current = null; + InputStream is = null; + try { + current = new java.io.File(".").getCanonicalPath(); + is = new URL("file:///" + current + filePath).openStream(); + ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + this.configurableProviders = mapper.readValue(is, ConfigurableProviders.class); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (is != null) is.close(); + } catch (IOException e) { + System.out.println("Warning: Could not close a stream after reading from file: " + filePath); + } } } @@ -100,4 +130,9 @@ public class DevelConfigLoader implements ConfigLoader { this.setDocument(); return document; } + + public ConfigurableProviders getConfigurableProviders() { + this.setConfigurableProviders(); + return configurableProviders; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java index 24a1053f6..c4bce7e49 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java @@ -1,6 +1,8 @@ package eu.eudat.logic.proxy.config.configloaders; +import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.logic.proxy.config.ExternalUrls; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviders; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; @@ -25,6 +27,7 @@ public class ProductionConfigLoader implements ConfigLoader { private ExternalUrls externalUrls; private List rdaProperties; private XWPFDocument document; + private ConfigurableProviders configurableProviders; @Autowired private Environment environment; @@ -85,6 +88,19 @@ public class ProductionConfigLoader implements ConfigLoader { } } + public void setConfigurableProviders() { + String filePath = environment.getProperty("configuration.configurable_login_providers"); + String current = null; + try { + current = new java.io.File(".").getCanonicalPath(); + InputStream is = new URL(Paths.get(filePath).toUri().toURL().toString()).openStream(); + ObjectMapper objectMapper = new ObjectMapper(); + this.configurableProviders = objectMapper.readValue(is, ConfigurableProviders.class); + } catch (IOException e) { + e.printStackTrace(); + } + } + public ExternalUrls getExternalUrls() { this.setExternalUrls(); return externalUrls; @@ -99,4 +115,9 @@ public class ProductionConfigLoader implements ConfigLoader { this.setDocument(); return document; } + + public ConfigurableProviders getConfigurableProviders() { + this.setConfigurableProviders(); + return configurableProviders; + } } \ 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 new file mode 100644 index 000000000..0f29de3fa --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProvider.java @@ -0,0 +1,11 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviderUserSettings; +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); +} 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 new file mode 100644 index 000000000..0edec977f --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderCustomProviderImpl.java @@ -0,0 +1,59 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviderUserSettings; +import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderResponseToken; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; + +@Component("configurableProviderCustomProvider") +public class ConfigurableProviderCustomProviderImpl implements ConfigurableProviderCustomProvider { + + @Override + public ConfigurableProviderResponseToken getAccessToken(String code, String redirectUri, String clientId, String clientSecret, String accessTokenUrl, + String grantType, String access_token, String expires_in) { + RestTemplate template = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap(); + + map.add("grant_type", grantType); + map.add("code", code); + map.add("redirect_uri", redirectUri); + map.add("client_id", clientId); + map.add("client_secret", clientSecret); + HttpEntity> request = new HttpEntity<>(map, headers); + + Map values = template.postForObject(accessTokenUrl, request, Map.class); + ConfigurableProviderResponseToken responseToken = new ConfigurableProviderResponseToken(); + responseToken.setAccessToken((String) values.get(access_token)); + responseToken.setExpiresIn((Integer) values.get(expires_in)); + + return responseToken; + } + + @Override + public ConfigurableProviderUser getUser(String accessToken, ConfigurableProviderUserSettings user) { + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = this.createBearerAuthHeaders(accessToken); + HttpEntity entity = new HttpEntity<>(headers); + + Map values = restTemplate.exchange(user.getUser_info_url(), HttpMethod.GET, entity, Map.class).getBody(); + return new ConfigurableProviderUser().getConfigurableProviderUser(values, user); + } + + private HttpHeaders createBearerAuthHeaders(String accessToken) { + return new HttpHeaders() {{ + String authHeader = "Bearer " + accessToken; + set("Authorization", authHeader); + }}; + } +} 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 new file mode 100644 index 000000000..f5a87e087 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/ConfigurableProviderUser.java @@ -0,0 +1,39 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviderUserSettings; + +import java.util.Map; + +public class ConfigurableProviderUser { + private String id; + private String name; + private String email; + + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + + ConfigurableProviderUser getConfigurableProviderUser(Map data, ConfigurableProviderUserSettings user) { + this.id = (String) data.get(user.getId()); + this.name = (String) data.get(user.getName()); + this.email = (String) data.get(user.getEmail()); + return this; + } +} 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 new file mode 100644 index 000000000..2e62f7007 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProvider.java @@ -0,0 +1,109 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; + +public class ConfigurableProvider { + + private boolean enabled; + private String configurableLoginId; + 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; + + public boolean getEnabled() { + return enabled; + } + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getConfigurableLoginId() { + return configurableLoginId; + } + public void setConfigurableLoginId(String configurableLoginId) { + this.configurableLoginId = configurableLoginId; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + 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 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/ConfigurableProviderToken.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderToken.java new file mode 100644 index 000000000..ac2021a82 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderToken.java @@ -0,0 +1,20 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; + +public class ConfigurableProviderToken { + private String access_token; + private String expires_in; + + public String getAccess_token() { + return access_token; + } + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public String getExpires_in() { + return expires_in; + } + public void setExpires_in(String expires_in) { + this.expires_in = 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/ConfigurableProviderUserSettings.java new file mode 100644 index 000000000..693957ca3 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviderUserSettings.java @@ -0,0 +1,36 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; + +public class ConfigurableProviderUserSettings { + private String id; + private String name; + private String email; + private String user_info_url; + + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + + public String getUser_info_url() { + return user_info_url; + } + public void setUser_info_url(String user_info_url) { + this.user_info_url = user_info_url; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviders.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviders.java new file mode 100644 index 000000000..01967ce32 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/entities/ConfigurableProviders.java @@ -0,0 +1,15 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.entities; + +import java.util.ArrayList; +import java.util.List; + +public class ConfigurableProviders { + private List providers = new ArrayList<>(); + + public List getProviders() { + return providers; + } + public void setProviders(List providers) { + this.providers = providers; + } +} 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 new file mode 100644 index 000000000..b09b002d2 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProviderModel.java @@ -0,0 +1,76 @@ +package eu.eudat.logic.security.customproviders.ConfigurableProvider.models; + +import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProvider; + +public class ConfigurableProviderModel { + + private String configurableLoginId; + private String name; + private String clientId; + private String redirect_uri; + private String oauthUrl; + private String scope; + private String state; + + public String getConfigurableLoginId() { + return configurableLoginId; + } + public void setConfigurableLoginId(String configurableLoginId) { + this.configurableLoginId = configurableLoginId; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + 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; + } + + public ConfigurableProviderModel fromDataModel(ConfigurableProvider entity) { + ConfigurableProviderModel model = new ConfigurableProviderModel(); + model.setConfigurableLoginId(entity.getConfigurableLoginId()); + 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()); + + 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 new file mode 100644 index 000000000..294a49323 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/customproviders/ConfigurableProvider/models/ConfigurableProvidersModel.java @@ -0,0 +1,29 @@ +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 java.util.LinkedList; +import java.util.List; + +public class ConfigurableProvidersModel { + private List providers; + + public List getProviders() { + return providers; + } + public void setProviders(List providers) { + this.providers = providers; + } + + public ConfigurableProvidersModel fromDataModel(ConfigurableProviders entity) { + ConfigurableProvidersModel model = new ConfigurableProvidersModel(); + List providerModelList = new LinkedList<>(); + for (ConfigurableProvider entityProvider : entity.getProviders()) { + if (entityProvider.getEnabled()) + providerModelList.add(new ConfigurableProviderModel().fromDataModel(entityProvider)); + } + model.setProviders(providerModelList); + return model; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java index aa764b0d5..9b594c96e 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/TokenValidatorFactoryImpl.java @@ -1,10 +1,13 @@ package eu.eudat.logic.security.validators; +import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.security.customproviders.B2Access.B2AccessCustomProvider; +import eu.eudat.logic.security.customproviders.ConfigurableProvider.ConfigurableProviderCustomProvider; import eu.eudat.logic.security.customproviders.LinkedIn.LinkedInCustomProvider; import eu.eudat.logic.security.customproviders.ORCID.ORCIDCustomProvider; import eu.eudat.logic.security.customproviders.OpenAIRE.OpenAIRECustomProvider; import eu.eudat.logic.security.validators.b2access.B2AccessTokenValidator; +import eu.eudat.logic.security.validators.configurableProvider.ConfigurableProviderTokenValidator; import eu.eudat.logic.security.validators.facebook.FacebookTokenValidator; import eu.eudat.logic.security.validators.google.GoogleTokenValidator; import eu.eudat.logic.security.validators.linkedin.LinkedInTokenValidator; @@ -20,7 +23,7 @@ import org.springframework.stereotype.Service; @Service("tokenValidatorFactory") public class TokenValidatorFactoryImpl implements TokenValidatorFactory { public enum LoginProvider { - GOOGLE(1), FACEBOOK(2), TWITTER(3), LINKEDIN(4), NATIVELOGIN(5), B2_ACCESS(6), ORCID(7), OPENAIRE(8); + GOOGLE(1), FACEBOOK(2), TWITTER(3), LINKEDIN(4), NATIVELOGIN(5), B2_ACCESS(6), ORCID(7), OPENAIRE(8), CONFIGURABLE(9); private int value; @@ -50,6 +53,8 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { return ORCID; case 8: return OPENAIRE; + case 9: + return CONFIGURABLE; default: throw new RuntimeException("Unsupported LoginProvider"); } @@ -62,18 +67,22 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { private ORCIDCustomProvider orcidCustomProvider; private LinkedInCustomProvider linkedInCustomProvider; private OpenAIRECustomProvider openAIRECustomProvider; + private ConfigurableProviderCustomProvider configurableProviderCustomProvider; + private ConfigLoader configLoader; @Autowired public TokenValidatorFactoryImpl( Environment environment, AuthenticationService nonVerifiedUserAuthenticationService, B2AccessCustomProvider b2AccessCustomProvider, - ORCIDCustomProvider orcidCustomProvider, LinkedInCustomProvider linkedInCustomProvider, OpenAIRECustomProvider openAIRECustomProvider) { + ORCIDCustomProvider orcidCustomProvider, LinkedInCustomProvider linkedInCustomProvider, OpenAIRECustomProvider openAIRECustomProvider, ConfigurableProviderCustomProvider configurableProviderCustomProvider, ConfigLoader configLoader) { this.environment = environment; this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; this.b2AccessCustomProvider = b2AccessCustomProvider; this.orcidCustomProvider = orcidCustomProvider; this.linkedInCustomProvider = linkedInCustomProvider; this.openAIRECustomProvider = openAIRECustomProvider; + this.configurableProviderCustomProvider = configurableProviderCustomProvider; + this.configLoader = configLoader; } public TokenValidator getProvider(LoginProvider provider) { @@ -92,6 +101,8 @@ public class TokenValidatorFactoryImpl implements TokenValidatorFactory { return new ORCIDTokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.orcidCustomProvider); case OPENAIRE: return new OpenAIRETokenValidator(this.environment, this.nonVerifiedUserAuthenticationService, this.openAIRECustomProvider); + case CONFIGURABLE: + return new ConfigurableProviderTokenValidator(this.configurableProviderCustomProvider, this.nonVerifiedUserAuthenticationService, this.configLoader); default: throw new RuntimeException("Login Provider Not Implemented"); } 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 new file mode 100644 index 000000000..f4b9fa2e8 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/ConfigurableProviderTokenValidator.java @@ -0,0 +1,60 @@ +package eu.eudat.logic.security.validators.configurableProvider; + +import eu.eudat.exceptions.security.NullEmailException; +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.validators.TokenValidator; +import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderRequest; +import eu.eudat.logic.security.validators.configurableProvider.helpers.ConfigurableProviderResponseToken; +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.springframework.stereotype.Component; + +import java.util.stream.Collectors; + +@Component("configurableProviderTokenValidator") +public class ConfigurableProviderTokenValidator implements TokenValidator { + + private ConfigurableProviderCustomProvider configurableProvider; + private AuthenticationService nonVerifiedUserAuthenticationService; + private ConfigLoader configLoader; + + public ConfigurableProviderTokenValidator(ConfigurableProviderCustomProvider configurableProvider, AuthenticationService nonVerifiedUserAuthenticationService, ConfigLoader configLoader) { + this.configurableProvider = configurableProvider; + this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; + this.configLoader = configLoader; + } + + public ConfigurableProviderResponseToken getAccessToken(ConfigurableProviderRequest configurableProviderRequest) { + ConfigurableProvider provider = 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()); + } + @Override + public Principal validateToken(LoginInfo credentials) throws NullEmailException { + String configurableLoginId = (String) credentials.getData(); + 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); + } + + private ConfigurableProvider getConfigurableProviderFromId(String configurableId) { + return this.configLoader.getConfigurableProviders().getProviders().stream() + .filter(prov -> prov.getConfigurableLoginId().equals(configurableId)) + .collect(Collectors.toList()) + .get(0); + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/helpers/ConfigurableProviderRequest.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/helpers/ConfigurableProviderRequest.java new file mode 100644 index 000000000..7877c88c3 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/helpers/ConfigurableProviderRequest.java @@ -0,0 +1,20 @@ +package eu.eudat.logic.security.validators.configurableProvider.helpers; + +public class ConfigurableProviderRequest { + private String code; + private String configurableLoginId; + + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } + + public String getConfigurableLoginId() { + return configurableLoginId; + } + public void setConfigurableLoginId(String configurableLoginId) { + this.configurableLoginId = configurableLoginId; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/helpers/ConfigurableProviderResponseToken.java b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/helpers/ConfigurableProviderResponseToken.java new file mode 100644 index 000000000..743dc376b --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/security/validators/configurableProvider/helpers/ConfigurableProviderResponseToken.java @@ -0,0 +1,20 @@ +package eu.eudat.logic.security.validators.configurableProvider.helpers; + +public class ConfigurableProviderResponseToken { + private String accessToken; + private Integer expiresIn; + + public String getAccessToken() { + return accessToken; + } + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public Integer getExpiresIn() { + return expiresIn; + } + public void setExpiresIn(Integer expiresIn) { + this.expiresIn = expiresIn; + } +} diff --git a/dmp-backend/web/src/main/resources/application-devel.properties b/dmp-backend/web/src/main/resources/application-devel.properties index 9b5597f62..f996c3546 100644 --- a/dmp-backend/web/src/main/resources/application-devel.properties +++ b/dmp-backend/web/src/main/resources/application-devel.properties @@ -18,6 +18,7 @@ pdf.converter.url=http://localhost:88/ configuration.externalUrls=/web/src/main/resources/ExternalUrls.xml configuration.rda=/web/src/main/resources/RDACommonStandards.txt configuration.h2020template=/web/src/main/resources/documents/h2020.docx +configuration.configurable_login_providers=/web/src/main/resources/configurableLoginProviders.json #############TWITTER LOGIN CONFIGURATIONS######### diff --git a/dmp-backend/web/src/main/resources/application-production.properties b/dmp-backend/web/src/main/resources/application-production.properties index 88f4d8554..2a5cb451e 100644 --- a/dmp-backend/web/src/main/resources/application-production.properties +++ b/dmp-backend/web/src/main/resources/application-production.properties @@ -19,6 +19,7 @@ pdf.converter.url=http://docsbox-web/ configuration.externalUrls=/tmp/ExternalUrls.xml configuration.rda=/tmp/RDACommonStandards.txt configuration.h2020template=/tmp/h2020.docx +configuration.configurable_login_providers= ####################SPRING MAIL CONFIGURATIONS################# spring.mail.default-encoding=UTF-8 diff --git a/dmp-backend/web/src/main/resources/application-staging.properties b/dmp-backend/web/src/main/resources/application-staging.properties index c35cd54ee..2f370c947 100644 --- a/dmp-backend/web/src/main/resources/application-staging.properties +++ b/dmp-backend/web/src/main/resources/application-staging.properties @@ -19,6 +19,7 @@ pdf.converter.url=http://docsbox-web/ configuration.externalUrls=/tmp/ExternalUrls.xml configuration.rda=/tmp/RDACommonStandards.txt configuration.h2020template=tmp/h2020.docx +configuration.configurable_login_providers= ####################INVITATION MAIL CONFIGURATIONS############## ####################GENERIC MAIL CONFIGURATIONS################# @@ -76,7 +77,6 @@ openaire.login.client_id= openaire.login.client_secret= openaire.login.access_token_url= openaire.login.redirect_uri= -openaire.login.userinfo_endpoint= #############ZENODO CONFIGURATIONS######### zenodo.url=https://sandbox.zenodo.org/api/ diff --git a/dmp-backend/web/src/main/resources/application.properties b/dmp-backend/web/src/main/resources/application.properties index 421636aa9..ddd1ca1f9 100644 --- a/dmp-backend/web/src/main/resources/application.properties +++ b/dmp-backend/web/src/main/resources/application.properties @@ -23,6 +23,7 @@ spring.mail.properties.mail.smtp.starttls.enable=false configuration.externalUrls= configuration.rda= configuration.h2020template= +configuration.configurable_login_providers= #############LOGIN CONFIGURATIONS######### #############GENERIC LOGIN CONFIGURATIONS######### diff --git a/dmp-backend/web/src/main/resources/configurableLoginProviders.json b/dmp-backend/web/src/main/resources/configurableLoginProviders.json new file mode 100644 index 000000000..af96394d8 --- /dev/null +++ b/dmp-backend/web/src/main/resources/configurableLoginProviders.json @@ -0,0 +1,27 @@ +{ + "providers": [ + { + "enabled": true, + "configurableLoginId": "myId", + "name": "myApp", + "clientId": "", + "clientSecret": "", + "redirect_uri": "", + "access_token_url": "", + "grant_type": "authorization_code", + "token": { + "access_token": "access_token", + "expires_in": "expires_in" + }, + "user": { + "id": "sub", + "name": "name", + "email": "email", + "user_info_endpoint": "" + }, + "oauthUrl": "/authorize", + "scope": "email", + "state": "123562" + } + ] +} diff --git a/dmp-frontend/src/app/core/common/enum/auth-provider.ts b/dmp-frontend/src/app/core/common/enum/auth-provider.ts index 3dcc29b9b..b8b70efd8 100644 --- a/dmp-frontend/src/app/core/common/enum/auth-provider.ts +++ b/dmp-frontend/src/app/core/common/enum/auth-provider.ts @@ -6,5 +6,6 @@ export enum AuthProvider { //NativeLogin=5, B2Access = 6, ORCID = 7, - OpenAire = 8 + OpenAire = 8, + Configurable = 9 } diff --git a/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts b/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts new file mode 100644 index 000000000..c8098f618 --- /dev/null +++ b/dmp-frontend/src/app/core/model/configurable-provider/configurableProvider.ts @@ -0,0 +1,9 @@ +export class ConfigurableProvider { + configurableLoginId: string; + name: string; + clientId: string; + redirect_uri: string; + oauthUrl: string; + scope: string; + state: string; +} 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 8f0ef3f5a..7eea789d2 100644 --- a/dmp-frontend/src/app/core/services/auth/auth.service.ts +++ b/dmp-frontend/src/app/core/services/auth/auth.service.ts @@ -1,7 +1,7 @@ -import {of as observableOf, throwError as observableThrowError, Observable } from 'rxjs'; +import { of as observableOf, throwError as observableThrowError, Observable } from 'rxjs'; -import {map, catchError, takeUntil } from 'rxjs/operators'; +import { map, catchError, takeUntil } from 'rxjs/operators'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -14,6 +14,7 @@ import { Credential } from '../../model/auth/credential'; import { LoginInfo } from '../../model/auth/login-info'; import { Principal } from '../../model/auth/Principal'; import { UiNotificationService, SnackBarNotificationLevel } from '../notification/ui-notification-service'; +import { ConfigurableProvider } from "../../model/configurable-provider/configurableProvider"; @Injectable() @@ -78,7 +79,7 @@ export class AuthService extends BaseService { catchError((error: any) => { //this.loginContextSubject.next(false); return observableThrowError(error); - }),); + })); } public nativeLogin(credentials: Credential): Observable { @@ -93,7 +94,7 @@ export class AuthService extends BaseService { catchError((error: any) => { //this.loginContextSubject.next(false); return observableThrowError(error); - }),); + })); } @@ -133,7 +134,7 @@ export class AuthService extends BaseService { const princ = this.current(); this.router.navigate(['/login']); return observableOf(princ); - }),); + })); } public onLogOutSuccess(logoutMessage: any) { @@ -145,4 +146,14 @@ export class AuthService extends BaseService { this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGOUT'), SnackBarNotificationLevel.Error); this.router.navigate(['/login']); } + + public getConfigurableProviders(): Observable { + const url = this.actionUrl + 'configurableLogin'; + return this.http.get(url, { headers: this.headers }).pipe( + map((res: any) => { + const providers = res.payload.providers; + return providers; + }) + ); + } } diff --git a/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.html b/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.html new file mode 100644 index 000000000..e69de29bb 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 new file mode 100644 index 000000000..278db1f85 --- /dev/null +++ b/dmp-frontend/src/app/ui/auth/login/configurable-login/configurable-login.component.ts @@ -0,0 +1,93 @@ +import { Component, OnInit, Input } from "@angular/core"; +import { BaseComponent } from "../../../../core/common/base/base.component"; +import { ActivatedRoute, Router, Params } from "@angular/router"; +import { HttpClient } from "@angular/common/http"; +import { LoginService } from "../utilities/login.service"; +import { AuthService } from "../../../../core/services/auth/auth.service"; +import { takeUntil } from "rxjs/operators"; +import { environment } from "../../../../../environments/environment"; +import { AuthProvider } from "../../../../core/common/enum/auth-provider"; +import { ConfigurableProvider } from "../../../../core/model/configurable-provider/configurableProvider"; +import { ConfigurableProvidersService } from "../utilities/configurableProviders.service"; + +@Component({ + selector: 'app-configurable-login', + templateUrl: './configurable-login.component.html', +}) +export class ConfigurableLoginComponent extends BaseComponent implements OnInit { + private returnUrl: string; + + // private configurableLoginId: string; + // private clientId: string; + // private oauthUrl: string; + // private redirectUri: string; + // private state: string; + // @Input() scope: string; + + private provider: ConfigurableProvider; + private providerId: string; + + constructor( + private route: ActivatedRoute, + private loginService: LoginService, + private authService: AuthService, + private router: Router, + private httpClient: HttpClient, + private providers: ConfigurableProvidersService + ) { + super(); + } + + ngOnInit(): void { + const params = this.route.snapshot.params; + this.providerId = params['id']; + if (this.providers.providers === undefined) { + this.authService.getConfigurableProviders() + .pipe(takeUntil(this._destroyed)) + .subscribe((data) => { + this.providers.providers = data; + this.provider = this.providers.providers.find(item => item.configurableLoginId == this.providerId) + this.route.queryParams + .pipe(takeUntil(this._destroyed)) + .subscribe((params: Params) => { + const returnUrlFromParams = params['returnUrl']; + if (returnUrlFromParams) { this.returnUrl = returnUrlFromParams; } + if (!params['code']) { this.configurableAuthorize(); } else { this.configurableLoginUser(params['code'], params['state']) } + }) + }); + } else { + this.provider = this.providers.providers.find(item => item.configurableLoginId == this.providerId) + this.route.queryParams + .pipe(takeUntil(this._destroyed)) + .subscribe((params: Params) => { + const returnUrlFromParams = params['returnUrl']; + if (returnUrlFromParams) { this.returnUrl = returnUrlFromParams; } + if (!params['code']) { this.configurableAuthorize(); } else { this.configurableLoginUser(params['code'], params['state']) } + }) + } + } + + public configurableAuthorize() { + window.location.href = this.provider.oauthUrl + + '?response_type=code&client_id=' + this.provider.clientId + + '&redirect_uri=' + this.provider.redirect_uri + this.providerId + + '&state=' + this.provider.state + + '&scope=' + this.provider.scope; + } + + public configurableLoginUser(code: string, state: string) { + if (state !== this.provider.state) { + this.router.navigate(['/login']) + } + this.httpClient.post(environment.Server + 'auth/configurableProviderRequestToken', { code: code, provider: AuthProvider.Configurable, configurableLoginId: this.providerId }) + .pipe(takeUntil(this._destroyed)) + .subscribe((data: any) => { + this.authService.login({ ticket: data.payload.accessToken, provider: AuthProvider.Configurable, data: { configurableLoginId: this.provider.configurableLoginId } }) + .pipe(takeUntil(this._destroyed)) + .subscribe( + res => this.loginService.onLogInSuccess(res, this.returnUrl), + error => this.loginService.onLogInError(error) + ) + }) + } +} 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 4540a7e80..562387685 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.component.html +++ b/dmp-frontend/src/app/ui/auth/login/login.component.html @@ -47,6 +47,11 @@ +
+ +