Unverified Commit a91cb7b8 authored by Cristina Yenyxe Gonzalez Garcia's avatar Cristina Yenyxe Gonzalez Garcia Committed by GitHub
Browse files

Merge pull request #10 from selvaebi/T2D-262

T2D-262 Created separate schemas for staging and prod. Auto notification for updates in registry.
parents ce54994b c63e0750
......@@ -6,14 +6,16 @@ pipeline {
}
environment {
stagingPostgresDbUrl = credentials('STAGINGPROPERTYREGISTRYDBURL')
fallBackPostgresDbUrl = credentials('STAGINGPROPERTYREGISTRYDBURL')
productionPostgresDbUrl = credentials('STAGINGPROPERTYREGISTRYDBURL')
fallBackPostgresDbUrl = credentials('PRODUCTIONPROPERTYREGISTRYDBURL')
productionPostgresDbUrl = credentials('PRODUCTIONPROPERTYREGISTRYDBURL')
postgresDBUserName = credentials('POSTGRESDBUSERNAME')
postgresDBPassword = credentials('POSTGRESDBPASSWORD')
tomcatCredentials = credentials('TOMCATCREDENTIALS')
stagingHost = credentials('STAGINGHOST')
fallbackHost = credentials('FALLBACKHOST')
productionHost = credentials('PRODUCTIONHOST')
smtpHost = credentials('SMTPHOST')
ampt2dEmailId = credentials('AMPT2DEMAILID')
}
parameters {
booleanParam(name: 'DeployToStaging' , defaultValue: false , description: '')
......@@ -22,7 +24,7 @@ pipeline {
stages {
stage('Default Build pointing to Staging DB') {
steps {
sh "mvn clean package -DskipTests -DbuildDirectory=staging/target -Dampt2d-property-registry-db.url=${stagingPostgresDbUrl} -Dampt2d-property-registry-db.username=${postgresDBUserName} -Dampt2d-property-registry-db.password=${postgresDBPassword}"
sh "mvn clean package -DskipTests -DbuildDirectory=staging/target -Dampt2d-property-registry-db.url=${stagingPostgresDbUrl} -Dampt2d-property-registry-db.username=${postgresDBUserName} -Dampt2d-property-registry-db.password=${postgresDBPassword} -Dsmtp-host=${smtpHost} -Damp-t2d-email-id=${ampt2dEmailId}"
}
}
stage('Build For FallBack And Production') {
......@@ -33,9 +35,9 @@ pipeline {
}
steps {
echo 'Build pointing to FallBack DB'
sh "mvn clean package -DskipTests -DbuildDirectory=fallback/target -Dampt2d-property-registry-db.url=${fallBackPostgresDbUrl} -Dampt2d-property-registry-db.username=${postgresDBUserName} -Dampt2d-property-registry-db.password=${postgresDBPassword}"
sh "mvn clean package -DskipTests -DbuildDirectory=fallback/target -Dampt2d-property-registry-db.url=${fallBackPostgresDbUrl} -Dampt2d-property-registry-db.username=${postgresDBUserName} -Dampt2d-property-registry-db.password=${postgresDBPassword} -Dsmtp-host=${smtpHost} -Damp-t2d-email-id=${ampt2dEmailId}"
echo 'Build pointing to Production DB'
sh "mvn clean package -DskipTests -DbuildDirectory=production/target -Dampt2d-property-registry-db.url=${productionPostgresDbUrl} -Dampt2d-property-registry-db.username=${postgresDBUserName} -Dampt2d-property-registry-db.password=${postgresDBPassword}"
sh "mvn clean package -DskipTests -DbuildDirectory=production/target -Dampt2d-property-registry-db.url=${productionPostgresDbUrl} -Dampt2d-property-registry-db.username=${postgresDBUserName} -Dampt2d-property-registry-db.password=${postgresDBPassword} -Dsmtp-host=${smtpHost} -Damp-t2d-email-id=${ampt2dEmailId}"
}
}
stage('Deploy To Staging') {
......@@ -46,7 +48,7 @@ pipeline {
}
steps {
echo 'Deploying to Staging'
sh "curl --upload-file staging/target/amp-t2d-property-registry-*.war 'http://'${tomcatCredentials}'@'${stagingHost}':8080/manager/text/deploy?path=/registry&update=true' | grep 'OK - Deployed application at context path '"
sh "curl --upload-file staging/target/amp-t2d-property-registry-*.war 'http://'${tomcatCredentials}'@'${stagingHost}':8080/manager/text/deploy?path=/dev/registry&update=true' | grep 'OK - Deployed application at context path '"
}
}
stage('Deploy To FallBack And Production') {
......
......@@ -31,6 +31,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
......
......@@ -20,8 +20,8 @@ package uk.ac.ebi.ampt2d.registry.config.security;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import uk.ac.ebi.ampt2d.registry.entities.User;
import uk.ac.ebi.ampt2d.registry.repositories.UserRepository;
import uk.ac.ebi.ampt2d.registry.entities.RegistryUser;
import uk.ac.ebi.ampt2d.registry.repositories.RegistryUserRepository;
import java.util.Arrays;
import java.util.List;
......@@ -29,21 +29,21 @@ import java.util.Map;
public class CustomAuthoritiesExtractor implements AuthoritiesExtractor {
private UserRepository userRepository;
private RegistryUserRepository registryUserRepository;
public CustomAuthoritiesExtractor(UserRepository userRepository) {
this.userRepository = userRepository;
public CustomAuthoritiesExtractor(RegistryUserRepository registryUserRepository) {
this.registryUserRepository = registryUserRepository;
}
@Override
public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
String email = (String) map.get("email");
User user = userRepository.findByEmail(email);
if (user == null) {
user = new User(email, User.Role.ROLE_USER);
userRepository.save(user);
return Arrays.asList(new SimpleGrantedAuthority(User.Role.ROLE_USER.name()));
RegistryUser registryUser = registryUserRepository.findByEmail(email);
if (registryUser == null) {
registryUser = new RegistryUser(email, RegistryUser.Role.ROLE_USER);
registryUserRepository.save(registryUser);
return Arrays.asList(new SimpleGrantedAuthority(RegistryUser.Role.ROLE_USER.name()));
}
return Arrays.asList(new SimpleGrantedAuthority(user.getRole().toString()));
return Arrays.asList(new SimpleGrantedAuthority(registryUser.getRole().toString()));
}
}
......@@ -17,7 +17,6 @@
*/
package uk.ac.ebi.ampt2d.registry.config.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.context.annotation.Bean;
......@@ -26,7 +25,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import uk.ac.ebi.ampt2d.registry.repositories.UserRepository;
import uk.ac.ebi.ampt2d.registry.repositories.RegistryUserRepository;
@ConditionalOnProperty(value = "security.enabled", havingValue = "true")
@Configuration
......@@ -57,7 +56,7 @@ public class EnableSecurityConfig extends ResourceServerConfigurerAdapter {
}
@Bean
public AuthoritiesExtractor authoritiesExtractor(UserRepository userRepository) {
return new CustomAuthoritiesExtractor(userRepository);
public AuthoritiesExtractor authoritiesExtractor(RegistryUserRepository registryUserRepository) {
return new CustomAuthoritiesExtractor(registryUserRepository);
}
}
\ No newline at end of file
/*
*
* Copyright 2018 EMBL - European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package uk.ac.ebi.ampt2d.registry.entities;
import uk.ac.ebi.ampt2d.registry.util.BeanUtil;
import uk.ac.ebi.ampt2d.registry.service.mail.MailService;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
public class EntityEventListener {
private enum Event {
CREATED,
UPDATED,
REMOVED
}
@PostPersist
public void onPersistEntity(IdentifiableEntity entity) {
sendNotification(entity, Event.CREATED);
}
@PostUpdate
public void onUpdateEntity(IdentifiableEntity entity) {
sendNotification(entity, Event.UPDATED);
}
@PostRemove
public void onRemoveEntity(IdentifiableEntity entity) {
sendNotification(entity, Event.REMOVED);
}
private void sendNotification(IdentifiableEntity entity, Event event) {
BeanUtil.getBean(MailService.class).send(entity.getClass().getSimpleName() + " " + entity.getId() + " " + event);
}
}
/*
*
* Copyright 2018 EMBL - European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package uk.ac.ebi.ampt2d.registry.entities;
public interface IdentifiableEntity<ENTITY_ID> {
ENTITY_ID getId();
}
\ No newline at end of file
......@@ -34,8 +34,8 @@ import javax.validation.constraints.Size;
import java.time.ZonedDateTime;
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Phenotype {
@EntityListeners({AuditingEntityListener.class, EntityEventListener.class})
public class Phenotype implements IdentifiableEntity<String> {
public enum Group {
......@@ -68,4 +68,8 @@ public class Phenotype {
@LastModifiedDate
private ZonedDateTime lastModifiedDate;
public String getId() {
return id;
}
}
......@@ -35,8 +35,8 @@ import javax.validation.constraints.Size;
import java.time.ZonedDateTime;
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Property {
@EntityListeners({AuditingEntityListener.class, EntityEventListener.class})
public class Property implements IdentifiableEntity<String> {
public enum Type {
......@@ -102,4 +102,7 @@ public class Property {
@LastModifiedDate
private ZonedDateTime lastModifiedDate;
public String getId() {
return id;
}
}
\ No newline at end of file
......@@ -24,8 +24,11 @@ import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
/*
User is a reserved word in PostgreSQL. Hence using class name RegistryUser
*/
@Entity
public class User {
public class RegistryUser {
public enum Role {
......@@ -43,10 +46,10 @@ public class User {
@Enumerated(EnumType.STRING)
private Role role;
public User() {
public RegistryUser() {
}
public User(String email, Role role) {
public RegistryUser(String email, Role role) {
this.email = email;
this.role = role;
}
......
/*
*
* Copyright 2018 EMBL - European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package uk.ac.ebi.ampt2d.registry.exceptionhandling;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.MailSendException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
@ControllerAdvice
public class ExceptionHandlers {
private static final Logger exceptionLogger = Logger.getLogger(ExceptionHandlers.class.getSimpleName());
@Value("${mail.from}")
private String mailFrom;
@ExceptionHandler(value = TransactionSystemException.class)
public ResponseEntity<String> handleMailSendException(TransactionSystemException ex) {
if (ex.getOriginalException().getCause().getClass().equals(MailSendException.class)) {
exceptionLogger.log(Level.SEVERE, ex.getOriginalException().getCause().getMessage());
return new ResponseEntity("An automated email could not be sent, please contact " + mailFrom,
HttpStatus.INTERNAL_SERVER_ERROR);
}
throw ex;
}
}
......@@ -19,11 +19,11 @@ package uk.ac.ebi.ampt2d.registry.repositories;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import uk.ac.ebi.ampt2d.registry.entities.User;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import uk.ac.ebi.ampt2d.registry.entities.RegistryUser;
@Repository
public interface UserRepository extends PagingAndSortingRepository<User, String> {
@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface RegistryUserRepository extends PagingAndSortingRepository<RegistryUser, String> {
User findByEmail(@Param("email") String email);
RegistryUser findByEmail(@Param("email") String email);
}
/*
*
* Copyright 2018 EMBL - European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package uk.ac.ebi.ampt2d.registry.service.mail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
@Service
public class MailService {
@Autowired
private JavaMailSender javaMailSender;
@Value("${mail.notify:false}")
private Boolean nofify;
@Value("${mail.to}")
private String to;
@Value("${mail.from}")
private String from;
@Value("${mail.subject}")
private String subject;
public void send(String text) {
if (nofify == true) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setFrom(from);
message.setSubject(subject);
message.setText(text);
javaMailSender.send(message);
}
}
}
\ No newline at end of file
/*
*
* Copyright 2018 EMBL - European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package uk.ac.ebi.ampt2d.registry.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
@Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
\ No newline at end of file
......@@ -21,4 +21,14 @@ security.enabled=true
security.oauth2.resource.userInfoUri=https://www.googleapis.com/userinfo/v2/me
security.oauth2.resource.filter-order=3
spring.mail.host=@smtp-host@
spring.mail.port=587
spring.mail.test-connection=false
spring.mail.properties.mail.smtp.auth=false
spring.mail.properties.mail.smtp.ssl.enable=false
mail.from=@amp-t2d-email-id@
mail.to=@amp-t2d-email-id@
mail.subject=AMP-T2D Registry Update
registry.protocols=https
\ No newline at end of file
insert into user VALUES ('selva@ebi.ac.uk','ROLE_ADMIN');
insert into user VALUES ('zl@ebi.ac.uk','ROLE_ADMIN');
insert into user VALUES ('iyangar@ebi.ac.uk','ROLE_ADMIN');
\ No newline at end of file
......@@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS property (
type VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS user (
CREATE TABLE IF NOT EXISTS registry_user (
email VARCHAR(255) NOT NULL,
role VARCHAR(255),
PRIMARY KEY (email)
......
......@@ -35,7 +35,7 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import uk.ac.ebi.ampt2d.registry.config.security.CustomAuthoritiesExtractor;
import uk.ac.ebi.ampt2d.registry.repositories.UserRepository;
import uk.ac.ebi.ampt2d.registry.repositories.RegistryUserRepository;
import java.io.Serializable;
import java.util.Collection;
......@@ -55,7 +55,7 @@ public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
private ClientDetailsService clientDetailsService;
@Autowired
private UserRepository userRepository;
private RegistryUserRepository registryUserRepository;
public RequestPostProcessor bearerToken(final String clientid) {
return mockRequest -> {
......@@ -69,7 +69,7 @@ public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
Map<String, Object> clientEmailMap = new HashMap<>();
clientEmailMap.put("email", clientId);
Collection<GrantedAuthority> authorities = authoritiesExtractor(userRepository).extractAuthorities(clientEmailMap);
Collection<GrantedAuthority> authorities = authoritiesExtractor(registryUserRepository).extractAuthorities(clientEmailMap);
Set<String> resourceIds = client.getResourceIds();
Set<String> scopes = client.getScope();
......@@ -99,7 +99,7 @@ public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
}
@Bean
public AuthoritiesExtractor authoritiesExtractor(UserRepository userRepository) {
return new CustomAuthoritiesExtractor(userRepository);
public AuthoritiesExtractor authoritiesExtractor(RegistryUserRepository registryUserRepository) {
return new CustomAuthoritiesExtractor(registryUserRepository);
}
}
\ No newline at end of file
......@@ -23,12 +23,18 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import uk.ac.ebi.ampt2d.registry.repositories.PhenotypeRepository;
import uk.ac.ebi.ampt2d.registry.repositories.PropertyRepository;
import uk.ac.ebi.ampt2d.registry.service.mail.MailService;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_CLASS;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
......@@ -38,8 +44,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "security.enabled=true")
@SpringBootTest(properties = {"security.enabled=true", "spring.jpa.hibernate.ddl-auto=none"})
@AutoConfigureMockMvc
@DirtiesContext(classMode = AFTER_CLASS)
public class PropertyRegistryServiceApplicationTests {
@Autowired
......@@ -54,8 +61,12 @@ public class PropertyRegistryServiceApplicationTests {
@Autowired
private PropertyRepository propertyRepository;
@MockBean
private MailService mailService;
@Before
public void deleteAllBeforeTests() throws Exception {
public void setUp() throws Exception {
doNothing().when(mailService).send(anyString());
phenotypeRepository.deleteAll();
propertyRepository.deleteAll();
}
......@@ -261,7 +272,7 @@ public class PropertyRegistryServiceApplicationTests {
mockMvc.perform(get("/phenotypes")).andExpect(status().isUnauthorized());
mockMvc.perform(get("/properties")).andExpect(status().isUnauthorized());
//AUTH_WHITELIST URLs not secured
// AUTH_WHITELIST URLs not secured
mockMvc.perform(get("/")).andExpect(status().isOk());
mockMvc.perform(get("/swagger-ui.html")).andExpect(status().isOk());
mockMvc.perform(get("/v2/api-docs")).andExpect(status().isOk());
......
/*
*
* Copyright 2018 EMBL - European Bioinformatics Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package uk.ac.ebi.ampt2d.registry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;