diff --git a/dmp-backend/pom.xml b/dmp-backend/pom.xml index a81d94abd..799574d81 100644 --- a/dmp-backend/pom.xml +++ b/dmp-backend/pom.xml @@ -7,10 +7,14 @@ 0.0.1-SNAPSHOT war - + 1.19.0 + 1.19.0 + 0.0.1-SNAPSHOT 4.3.8.RELEASE + + 4.2.3.RELEASE 1.19.1 7.0.35 5.2.9.Final @@ -203,6 +207,54 @@ runtime + + + + + + + + + + + + com.google.apis + google-api-services-oauth2 + v2-rev75-1.19.0 + + + com.google.http-client + google-http-client-jackson2 + ${project.http.version} + + + com.google.oauth-client + google-oauth-client-jetty + ${project.oauth.version} + + + + + + + org.springframework.security + spring-security-web + ${org.springframework.security.version} + + + org.springframework.security + spring-security-config + ${org.springframework.security.version} + + + + org.springframework + spring-tx + ${org.springframework.version} + + + + org.apache.commons @@ -211,8 +263,6 @@ - - diff --git a/dmp-backend/src/main/java/dao/entities/security/UserInfoDao.java b/dmp-backend/src/main/java/dao/entities/security/UserInfoDao.java new file mode 100644 index 000000000..0922b1fbc --- /dev/null +++ b/dmp-backend/src/main/java/dao/entities/security/UserInfoDao.java @@ -0,0 +1,12 @@ +package dao.entities.security; + +import java.util.UUID; + +import dao.Dao; +import entities.security.UserInfo; + +public interface UserInfoDao extends Dao { + + public UserInfo getByKey(String id, String email); + +} \ No newline at end of file diff --git a/dmp-backend/src/main/java/dao/entities/security/UserInfoDaoImpl.java b/dmp-backend/src/main/java/dao/entities/security/UserInfoDaoImpl.java new file mode 100644 index 000000000..3f257d8f5 --- /dev/null +++ b/dmp-backend/src/main/java/dao/entities/security/UserInfoDaoImpl.java @@ -0,0 +1,37 @@ +package dao.entities.security; + +import java.util.List; +import java.util.UUID; + +import javax.persistence.NoResultException; +import javax.persistence.TypedQuery; + +import dao.JpaDao; +import entities.security.UserInfo; + + +public class UserInfoDaoImpl extends JpaDao implements UserInfoDao { + + public UserInfo loadDetails(UserInfo t) { + // TODO Auto-generated method stub + return null; + } + + + + @Override + public UserInfo getByKey(String id, String email) { + String queryString = "FROM UserInfo userInfo where userInfo.id = :userInfoID and userInfo.email = :userInfoEmail"; + TypedQuery typedQuery = entityManager.createQuery(queryString, UserInfo.class); + typedQuery.setParameter("userInfoID", id); + typedQuery.setParameter("userInfoEmail", email); + try { + return typedQuery.getSingleResult(); + } + catch(NoResultException ex) { + return null; + } + } + + +} \ No newline at end of file diff --git a/dmp-backend/src/main/java/entities/security/UserInfo.java b/dmp-backend/src/main/java/entities/security/UserInfo.java new file mode 100644 index 000000000..1e95a0c21 --- /dev/null +++ b/dmp-backend/src/main/java/entities/security/UserInfo.java @@ -0,0 +1,151 @@ +package entities.security; + +import java.io.Serializable; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Type; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + + +@Entity +@Table(name="\"UserInfo\"") +@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="autoid") +public class UserInfo implements Serializable{ + + private static final long serialVersionUID = 1225151430484658395L; + + @Id + @GeneratedValue + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(name = "autoid", updatable = false, nullable = false, columnDefinition = "BINARY(16)") + private UUID autoid; + + //required + @Column(name = "id") + String id = null; + @Column(name = "email") + String email = null; + + //non required + @Column(name = "\"emailIsVerified\"", nullable = true) + Boolean emailIsVerified = null; + @Column(name = "name", nullable = true) + String name = null; + @Column(name = "\"pictureUrl\"", nullable = true) + String pictureUrl = null; + @Column(name = "locale", nullable = true) + String locale = null; + @Column(name = "\"familyName\"", nullable = true) + String familyName = null; + @Column(name = "\"givenName\"", nullable = true) + String givenName = null; + + + @Type(type="typedefinition.XMLType") + @Column(name = "additionalinfo", columnDefinition = "xml", nullable = true) + private String additionalinfo; + + + + public UserInfo () {} + + public UserInfo(String id, String email, Boolean emailIsVerified, String name, String pictureUrl, String locale, String familyName, String givenName, String additionalinfo) { + this.id = id; + this.email = email; + this.emailIsVerified = emailIsVerified; + this.name = name; + this.pictureUrl = pictureUrl; + this.locale = locale; + this.familyName = familyName; + this.givenName = givenName; + this.additionalinfo = additionalinfo; + } + + + + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + public boolean isEmailIsVerified() { + return emailIsVerified; + } + public void setEmailIsVerified(boolean emailIsVerified) { + this.emailIsVerified = emailIsVerified; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getPictureUrl() { + return pictureUrl; + } + public void setPictureUrl(String pictureUrl) { + this.pictureUrl = pictureUrl; + } + public String getLocale() { + return locale; + } + public void setLocale(String locale) { + this.locale = locale; + } + public String getFamilyName() { + return familyName; + } + public void setFamilyName(String familyName) { + this.familyName = familyName; + } + public String getGivenName() { + return givenName; + } + public void setGivenName(String givenName) { + this.givenName = givenName; + } + + + public Boolean getEmailIsVerified() { + return emailIsVerified; + } + + public void setEmailIsVerified(Boolean emailIsVerified) { + this.emailIsVerified = emailIsVerified; + } + + public String getAdditionalinfo() { + return additionalinfo; + } + + public void setAdditionalinfo(String additionalinfo) { + this.additionalinfo = additionalinfo; + } + + @Override + public String toString() { + return "UserInfo [id=" + id + ", email=" + email + ", emailIsVerified=" + emailIsVerified + + ", name=" + name + ", pictureUrl=" + pictureUrl + ", locale=" + locale + ", familyName=" + familyName + + ", givenName=" + givenName + ", additionalinfo=" + additionalinfo + "]"; + } + + + + +} diff --git a/dmp-backend/src/main/java/exceptions/NonValidTokenException.java b/dmp-backend/src/main/java/exceptions/NonValidTokenException.java new file mode 100644 index 000000000..0d5273f5c --- /dev/null +++ b/dmp-backend/src/main/java/exceptions/NonValidTokenException.java @@ -0,0 +1,11 @@ +package exceptions; + +public class NonValidTokenException extends Exception { + + private static final long serialVersionUID = -2834659827755141154L; + + public NonValidTokenException(String msg) { + super(msg); + } + +} diff --git a/dmp-backend/src/main/java/rest/BackendInterface.java b/dmp-backend/src/main/java/rest/BackendInterface.java index 5cbfa3a69..c31a769d1 100644 --- a/dmp-backend/src/main/java/rest/BackendInterface.java +++ b/dmp-backend/src/main/java/rest/BackendInterface.java @@ -76,6 +76,7 @@ public class BackendInterface { + // FETCH BY DMP(S) @RequestMapping(method = RequestMethod.GET, value = { "/DMP" }, produces="text/plain") diff --git a/dmp-backend/src/main/java/security/CustomAuthenticationProvider.java b/dmp-backend/src/main/java/security/CustomAuthenticationProvider.java new file mode 100644 index 000000000..dd797a0bc --- /dev/null +++ b/dmp-backend/src/main/java/security/CustomAuthenticationProvider.java @@ -0,0 +1,60 @@ +package security; + +import java.util.ArrayList; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +import dao.entities.security.UserInfoDao; +import entities.security.UserInfo; +import exceptions.NonValidTokenException; + +@Component +public class CustomAuthenticationProvider implements AuthenticationProvider { + + + @Autowired private UserInfoDao userInfoDao; + + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + + + if (authentication != null) { + // check whether the token is valid + String token = (String)authentication.getCredentials(); + GoogleTokenValidator gValidator = new GoogleTokenValidator(); + UserInfo userInfo = null; + try { + userInfo = gValidator.validateToken(token); + } catch (NonValidTokenException e) { + System.out.println("Could not validate a user by his token! Reason: "+e.getMessage()); + throw new AuthenticationServiceException("Token validation failed - Not a valid token"); + } + //store to database if new + UserInfo existingUserInfo = userInfoDao.getByKey(userInfo.getId(), userInfo.getEmail()); + if(existingUserInfo == null) + userInfoDao.create(userInfo); + + // if reached this point, authentication is ok + return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), new ArrayList<>()); + } + else + throw new AuthenticationServiceException("Authentication failed"); + + + + } + + @Override + public boolean supports(Class authentication) { + return authentication.equals(UsernamePasswordAuthenticationToken.class); + } + +} \ No newline at end of file diff --git a/dmp-backend/src/main/java/security/GoogleTokenValidator.java b/dmp-backend/src/main/java/security/GoogleTokenValidator.java new file mode 100644 index 000000000..8e133d93c --- /dev/null +++ b/dmp-backend/src/main/java/security/GoogleTokenValidator.java @@ -0,0 +1,74 @@ +package security; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.List; + +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; + +import entities.security.UserInfo; +import exceptions.NonValidTokenException; + +public class GoogleTokenValidator { + + private static final JacksonFactory jacksonFactory = new JacksonFactory(); + private static final HttpTransport transport = new NetHttpTransport(); + + private static final List clientIDs = Arrays.asList("1010962018903-glegmqudqtl1lub0150vacopbu06lgsg.apps.googleusercontent.com"); + + private GoogleIdTokenVerifier verifier = null; + + + public GoogleTokenValidator() { + verifier = new GoogleIdTokenVerifier.Builder(transport, jacksonFactory) + .setAudience(clientIDs) + // Or, if multiple clients access the backend: + //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3)) + .build(); + } + + + public UserInfo validateToken(String token) throws NonValidTokenException { + + GoogleIdToken idToken = null; + try { + idToken = verifier.verify(token); + } + catch(GeneralSecurityException ex) { + throw new NonValidTokenException("Token is not valid -> "+ex.getMessage()); + } + catch(IOException ex) { + throw new NonValidTokenException("Could not verify token -> "+ex.getMessage()); + } + catch(IllegalArgumentException ex) { + throw new NonValidTokenException("Could not verify token"); + } + + + if (idToken != null) { + Payload payload = idToken.getPayload(); + + UserInfo userInfo = new UserInfo(payload.getSubject(), payload.getEmail(), + payload.getEmailVerified(), (String)payload.get("name"), (String)payload.get("picture"), + (String)payload.get("locale"), (String)payload.get("family_name"), (String)payload.get("given_name"), ""); + +// System.out.println(userInfo.toString()); + + return userInfo; + + } else { + throw new NonValidTokenException("Not a valid token"); + } + } + + + + + +} diff --git a/dmp-backend/src/main/java/security/TokenAuthenticationFilter.java b/dmp-backend/src/main/java/security/TokenAuthenticationFilter.java new file mode 100644 index 000000000..f9e968e33 --- /dev/null +++ b/dmp-backend/src/main/java/security/TokenAuthenticationFilter.java @@ -0,0 +1,35 @@ +package security; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.GenericFilterBean; + + +public class TokenAuthenticationFilter extends GenericFilterBean { + + private static final String HEADER_TOKEN_FIELD = "oauth2-token"; + + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { + + final HttpServletRequest httpRequest = (HttpServletRequest) request; + + String accessToken = httpRequest.getHeader(HEADER_TOKEN_FIELD); + if(accessToken==null) accessToken = ""; + //just pass the token into the credentials object of the UsernamePasswordAuthenticationToken class + final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("google-user", accessToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + + chain.doFilter(request, response); + } + +} diff --git a/dmp-backend/src/main/webapp/WEB-INF/applicationContext.xml b/dmp-backend/src/main/webapp/WEB-INF/applicationContext.xml index f50459666..0f6721e4b 100644 --- a/dmp-backend/src/main/webapp/WEB-INF/applicationContext.xml +++ b/dmp-backend/src/main/webapp/WEB-INF/applicationContext.xml @@ -29,7 +29,15 @@ - + + + + + + + + + @@ -87,6 +95,7 @@ + diff --git a/dmp-backend/src/main/webapp/WEB-INF/dmp.properties b/dmp-backend/src/main/webapp/WEB-INF/dmp.properties index a904f979d..5fe107785 100644 --- a/dmp-backend/src/main/webapp/WEB-INF/dmp.properties +++ b/dmp-backend/src/main/webapp/WEB-INF/dmp.properties @@ -5,9 +5,9 @@ ##########################Persistence########################################## persistence.jdbc.driver = org.postgresql.Driver -persistence.jdbc.url = jdbc:postgresql://localhost:5432/db -persistence.dbusername = db-user -persistence.dbpassword = db-pass +persistence.jdbc.url = jdbc:postgresql://develdb1.madgik.di.uoa.gr:5432/dmptool +persistence.dbusername = dmptool +persistence.dbpassword = dmpt00lu$r ##########################/Persistence########################################## @@ -42,5 +42,4 @@ persistence.hibernate.connectionpool.c3p0.idle_connection_test_period = 3600 persistence.hibernate.connectionpool.c3p0.test_connection_on_checkin = true persistence.hibernate.connectionpool.c3p0.test_connection_on_checkout = false persistence.hibernate.connectionpool.c3p0.preferred_test_query = select 1 -########################Persistence/Hibernate/Connection pool#################### - +########################Persistence/Hibernate/Connection pool#################### \ No newline at end of file diff --git a/dmp-backend/src/main/webapp/WEB-INF/spring-security.xml b/dmp-backend/src/main/webapp/WEB-INF/spring-security.xml new file mode 100644 index 000000000..28e58abf8 --- /dev/null +++ b/dmp-backend/src/main/webapp/WEB-INF/spring-security.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dmp-backend/src/main/webapp/WEB-INF/web.xml b/dmp-backend/src/main/webapp/WEB-INF/web.xml index f900b4a52..0c3aa66c5 100644 --- a/dmp-backend/src/main/webapp/WEB-INF/web.xml +++ b/dmp-backend/src/main/webapp/WEB-INF/web.xml @@ -31,11 +31,24 @@ contextConfigLocation - - /WEB-INF/applicationContext.xml - + /WEB-INF/applicationContext.xml,/WEB-INF/spring-security.xml + 30 + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + springSecurityFilterChain + /* + + + + + \ No newline at end of file diff --git a/dmp-db-scema/DataManagementPlanDB.sql b/dmp-db-scema/DataManagementPlanDB.sql index 6bfa4beff..2168050f7 100644 --- a/dmp-db-scema/DataManagementPlanDB.sql +++ b/dmp-db-scema/DataManagementPlanDB.sql @@ -1,4 +1,3 @@ - SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; @@ -26,6 +25,7 @@ drop table if exists "Registry" cascade; drop table if exists "DatasetService" cascade; drop table if exists "DatasetRegistry" cascade; drop table if exists "DatasetDataRepository" cascade; +drop table if exists "UserInfo" cascade; -- CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; @@ -420,12 +420,12 @@ CREATE TABLE "DatasetDataRepository" ( "ID" uuid DEFAULT uuid_generate_v4() NOT NULL ); + ALTER TABLE "DatasetDataRepository" OWNER TO dmptool; COMMENT ON TABLE "DatasetDataRepository" IS 'Linking Dataset to DataRepository'; - ALTER TABLE ONLY "DatasetDataRepository" ADD CONSTRAINT "DatasetDataRepositoryDatasetReference" FOREIGN KEY ("Dataset") REFERENCES "Dataset"("ID"); @@ -447,6 +447,21 @@ ALTER TABLE ONLY "DatasetService" ADD CONSTRAINT "DatasetServiceServiceReference" FOREIGN KEY ("Service") REFERENCES "Service"("ID"); +CREATE TABLE "UserInfo" ( + "autoid" uuid DEFAULT uuid_generate_v4() NOT NULL, + "id" character varying(500), + "email" character varying(250), + "emailIsVerified" boolean, + "name" character varying(250), + "pictureUrl" character varying(500), + "locale" character varying(50), + "familyName" character varying(250), + "givenName" character varying(250), + "additionalinfo" xml, + PRIMARY KEY (id, email) +); + + REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON SCHEMA public FROM postgres; GRANT ALL ON SCHEMA public TO postgres;