Skip to content

Commit

Permalink
Oauth using EGA AAI and custom Role Based authority
Browse files Browse the repository at this point in the history
  • Loading branch information
selvaebi committed Mar 20, 2019
1 parent f996565 commit 4912b4d
Show file tree
Hide file tree
Showing 16 changed files with 477 additions and 7 deletions.
21 changes: 18 additions & 3 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pipeline {
stagingHost = credentials('STAGINGHOST')
fallbackHost = credentials('FALLBACKHOST')
productionHost = credentials('PRODUCTIONHOST')
egaAAIClientId = credentials('EGAAAICLIENTID')
egaAAIClientSecret = credentials('EGAAAICLIENTSECRET')
egaAAITokenIntrospectUrl = credentials('EGAAAITOKENINTROSPECTURL')
}
parameters {
choice(choices: ['validate', 'create'], description: 'Behaviour at connection time (initialize/validate schema)',
Expand All @@ -24,7 +27,11 @@ pipeline {
stages {
stage('Default Build pointing to Staging DB') {
steps {
sh "mvn clean package -DskipTests -DbuildDirectory=staging/target -DegaAccession-db.url=${stagingPostgresDbUrl} -DegaAccession-db.username=${postgresDBUserName} -DegaAccession-db.password=${postgresDBPassword} -Dinstance.id=ega-accession-01-staging -Dddl-behaviour=${params.DbBehaviour}"
sh "mvn clean package -DskipTests -DbuildDirectory=staging/target \
-DegaAccession-db.url=${stagingPostgresDbUrl} -DegaAccession-db.username=${postgresDBUserName} \
-DegaAccession-db.password=${postgresDBPassword} -Dinstance.id=ega-accession-01-staging \
-Dddl-behaviour=${params.DbBehaviour} -Dega-aai-client-id=${egaAAIClientId} \
-Dega-aai-client-secret=${egaAAIClientSecret} -Dega-aai-token-introspect-url=${egaAAITokenIntrospectUrl}"
}
}
stage('Build For FallBack And Production') {
Expand All @@ -35,9 +42,17 @@ pipeline {
}
steps {
echo 'Build pointing to FallBack DB'
sh "mvn clean package -DskipTests -DbuildDirectory=fallback/target -DegaAccession-db.url=${fallBackPostgresDbUrl} -DegaAccession-db.username=${postgresDBUserName} -DegaAccession-db.password=${postgresDBPassword} -Dinstance.id=ega-accession-01-fallback -Dddl-behaviour=${params.DbBehaviour}"
sh "mvn clean package -DskipTests -DbuildDirectory=fallback/target \
-DegaAccession-db.url=${fallBackPostgresDbUrl} -DegaAccession-db.username=${postgresDBUserName} \
-DegaAccession-db.password=${postgresDBPassword} -Dinstance.id=ega-accession-01-fallback \
-Dddl-behaviour=${params.DbBehaviour} -Dega-aai-client-id=${egaAAIClientId} \
-Dega-aai-client-secret=${egaAAIClientSecret} -Dega-aai-token-introspect-url=${egaAAITokenIntrospectUrl}"
echo 'Build pointing to Production DB'
sh "mvn clean package -DskipTests -DbuildDirectory=production/target -DegaAccession-db.url=${productionPostgresDbUrl} -DegaAccession-db.username=${postgresDBUserName} -DegaAccession-db.password=${postgresDBPassword} -Dinstance.id=ega-accession-01-production -Dddl-behaviour=${params.DbBehaviour}"
sh "mvn clean package -DskipTests -DbuildDirectory=production/target \
-DegaAccession-db.url=${productionPostgresDbUrl} -DegaAccession-db.username=${postgresDBUserName} \
-DegaAccession-db.password=${postgresDBPassword} -Dinstance.id=ega-accession-01-production \
-Dddl-behaviour=${params.DbBehaviour} -Dega-aai-client-id=${egaAAIClientId} \
-Dega-aai-client-secret=${egaAAIClientSecret} -Dega-aai-token-introspect-url=${egaAAITokenIntrospectUrl}"
}
}
stage('Deploy To Staging') {
Expand Down
6 changes: 5 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>uk.ac.ebi.ega</groupId>
<artifactId>accessioning-service</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>${packaging.type}</packaging>
<packaging>war</packaging>

<name>accessioning-service</name>
<url>http://github.com/EBIvariation/ega-accession</url>
Expand Down Expand Up @@ -35,6 +35,10 @@
<version>0.6-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
*
* Copyright 2019 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.ega.accession.configuration;

import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import uk.ac.ebi.ega.accession.user.AccessioningUser;
import uk.ac.ebi.ega.accession.user.AccessioningUserRepository;

@Configuration
@EntityScan(basePackageClasses = AccessioningUser.class)
@EnableJpaRepositories(basePackageClasses = AccessioningUserRepository.class)
public class AccessioningUserConfiguration {
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@
import springfox.documentation.schema.AlternateTypeRule;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.Tag;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
Expand All @@ -55,7 +59,7 @@
@Configuration
@EnableSwagger2
@EnableConfigurationProperties(SwaggerApiInfoProperties.class)
@Import({BeanValidatorPluginsConfiguration.class})
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {

@Autowired
Expand All @@ -64,6 +68,12 @@ public class SwaggerConfiguration {
@Autowired
private SwaggerApiInfoProperties swaggerApiInfoProperties;

private SecurityReference securityReference = SecurityReference.builder()
.reference("Authorization").scopes(new AuthorizationScope[0]).build();

private SecurityContext securityContext = SecurityContext.builder()
.securityReferences(Arrays.asList(securityReference)).build();

@Bean
public Docket metadataApi() {
return new Docket(DocumentationType.SWAGGER_2)
Expand Down Expand Up @@ -91,6 +101,8 @@ public Docket metadataApi() {
.globalResponseMessage(RequestMethod.PATCH, getResponseMessagesForPostAndPatch())
.directModelSubstitute(LocalDate.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.securitySchemes(Arrays.asList(new ApiKey("Authorization", "Authorization", "header")))
.securityContexts(Arrays.asList(securityContext))
.alternateTypeRules(getSubstitutionRules());
}

Expand Down Expand Up @@ -121,7 +133,8 @@ private Predicate<String> getScanRestServicesPathPredicate() {
return Predicates.and(
Predicates.not(PathSelectors.regex("/actuator.*")), // Hide spring-actuator
Predicates.not(PathSelectors.regex("/error.*")), // Hide spring-data error
Predicates.not(PathSelectors.regex("/profile.*")) // Hide spring-data profile
Predicates.not(PathSelectors.regex("/profile.*")), // Hide spring-data profile
Predicates.not(PathSelectors.regex("/users.*")) // Hide user controller from swagger
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
*
* Copyright 2019 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.ega.accession.configuration.security;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import uk.ac.ebi.ega.accession.user.AccessioningUser;
import uk.ac.ebi.ega.accession.user.AccessioningUserRepository;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* This class provides custom authorities for the EGA AAI authenticated user.
*/
public class CustomUserAuthenticationConverter implements UserAuthenticationConverter {

private final static String USER_ID = "user_id";

private AccessioningUserRepository accessioningUserRepository;

public CustomUserAuthenticationConverter(AccessioningUserRepository accessioningUserRepository) {
this.accessioningUserRepository = accessioningUserRepository;
}

public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put(USER_ID, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}

public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USER_ID)) {
Object principal = map.get(USER_ID);
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}

private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
String userId = (String) map.get(USER_ID);
AccessioningUser accessioningUser = accessioningUserRepository.findByUserId(userId);
if (accessioningUser == null) {
accessioningUser = new AccessioningUser(userId, AccessioningUser.Role.ROLE_USER);
accessioningUserRepository.save(accessioningUser);
return Arrays.asList(new SimpleGrantedAuthority(AccessioningUser.Role.ROLE_USER.name()));
}
return Arrays.asList(new SimpleGrantedAuthority(accessioningUser.getRole().toString()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
*
* Copyright 2019 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.ega.accession.configuration.security;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
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;

@ConditionalOnProperty(value = "security.enabled", havingValue = "false")
@Configuration
@EnableResourceServer
public class DisableSecurityConfig extends ResourceServerConfigurerAdapter {

@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
*
* Copyright 2019 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.ega.accession.configuration.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
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 org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import uk.ac.ebi.ega.accession.user.AccessioningUserRepository;

@ConditionalOnProperty(value = "security.enabled", havingValue = "true")
@Configuration
@EnableResourceServer
public class EnableSecurityConfig extends ResourceServerConfigurerAdapter {

private static final String[] AUTH_WHITELIST = {
// -- swagger ui
"/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/swagger-ui.html",
"/webjars/**",
"/"
};

@Autowired
private RemoteTokenServices remoteTokenServices;

@Autowired
private AccessioningUserRepository accessioningUserRepository;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
DefaultAccessTokenConverter defaultAccessTokenConverter = new DefaultAccessTokenConverter();
defaultAccessTokenConverter.setUserTokenConverter(new CustomUserAuthenticationConverter(accessioningUserRepository));
remoteTokenServices.setAccessTokenConverter(defaultAccessTokenConverter);
}

@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.POST).hasAnyRole("EDITOR", "ADMIN")
.antMatchers(HttpMethod.PUT).hasAnyRole("EDITOR", "ADMIN")
.antMatchers(HttpMethod.PATCH).hasAnyRole("EDITOR", "ADMIN")
.antMatchers(HttpMethod.DELETE).hasAnyRole("EDITOR", "ADMIN")
.anyRequest().authenticated();
}
}
Loading

0 comments on commit 4912b4d

Please sign in to comment.