Spring security library providing preauthorize and authenticated methods for apis

This commit is contained in:
Konstantinos Triantafyllou 2020-09-03 19:40:48 +00:00
parent dd8f9bfe45
commit bf383fc29c
15 changed files with 569 additions and 0 deletions

68
pom.xml Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>eu.dnetlib</groupId>
<artifactId>uoa-authorization-library</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>uoa-authorization-library</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Starter for using Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
</plugins>
<finalName>uoa-authorization-library</finalName>
</build>
</project>

View File

@ -0,0 +1,13 @@
package eu.dnetlib.uoaauthorizationlibrary.configuration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.*;
@Configuration
@PropertySources({
@PropertySource("classpath:authorization.properties"),
@PropertySource(value = "classpath:dnet-override.properties", ignoreResourceNotFound = true)
})
@EnableConfigurationProperties({SecurityConfig.class})
@ComponentScan(basePackages = { "eu.dnetlib.uoaauthorizationlibrary" })
public class AuthorizationConfiguration { }

View File

@ -0,0 +1,27 @@
package eu.dnetlib.uoaauthorizationlibrary.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("authorization.security")
public class SecurityConfig {
private String userInfoUrl;
private String originServer;
public String getUserInfoUrl() {
return userInfoUrl;
}
public void setUserInfoUrl(String userInfoUrl) {
this.userInfoUrl = userInfoUrl;
}
public String getOriginServer() {
return originServer;
}
public void setOriginServer(String originServer) {
this.originServer = originServer;
}
}

View File

@ -0,0 +1,54 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import eu.dnetlib.uoaauthorizationlibrary.utils.AuthorizationUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class AuthorizationFilter implements Filter {
private AuthorizationProvider authorizationProvider;
private AuthorizationUtils utils;
private final Logger log = Logger.getLogger(this.getClass());
@Autowired
AuthorizationFilter(AuthorizationProvider authorizationProvider, AuthorizationUtils utils) {
this.authorizationProvider = authorizationProvider;
this.utils = utils;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String token = utils.getToken(request);
if (token != null) {
Authentication auth = authorizationProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
} else {
HttpServletResponse response = (HttpServletResponse) res;
response.sendError(HttpStatus.UNAUTHORIZED.value(), "No token has been found");
return;
}
filterChain.doFilter(req, res);
}
@Override
public void destroy() {
}
}

View File

@ -0,0 +1,33 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import eu.dnetlib.uoaauthorizationlibrary.utils.AuthorizationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
public class AuthorizationFilterConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private AuthorizationProvider authorizationProvider;
private AuthorizationUtils utils;
@Override
public void init(HttpSecurity http) throws Exception {
http.csrf().disable();
}
@Autowired
public AuthorizationFilterConfigurer(AuthorizationProvider authorizationProvider, AuthorizationUtils utils) {
this.authorizationProvider = authorizationProvider;
this.utils = utils;
}
@Override
public void configure(HttpSecurity http) throws Exception {
AuthorizationFilter customFilter = new AuthorizationFilter(authorizationProvider, utils);
http.addFilterBefore(customFilter, BasicAuthenticationFilter.class);
}
}

View File

@ -0,0 +1,23 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
@Component
public class AuthorizationProvider {
private UserDetailsServiceImpl userDetailsService;
@Autowired
AuthorizationProvider(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = userDetailsService.loadUserByUsername(token);
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
}

View File

@ -0,0 +1,37 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import org.springframework.stereotype.Component;
@Component(value = "AuthorizationService")
public class AuthorizationService {
public final String SUPER_ADMIN = "SUPER_ADMINISTRATOR";
public final String PORTAL_ADMIN = "PORTAL_ADMINISTRATOR";
public final String USER_ADMIN = "USER_MANAGER";
/**
* Type = FUNDER | COMMUNITY | INSTITUTION | PROJECT
*
* */
public String curator(String type) {
return type.toUpperCase() + "_CURATOR";
}
/**
* Type = FUNDER | COMMUNITY | INSTITUTION | PROJECT
*
* Id = EE, EGI, etc
* */
public String manager(String type, String id) {
return type.toUpperCase() + "_" + id.toUpperCase() + "_MANAGER";
}
/**
* Type = FUNDER | COMMUNITY | INSTITUTION | PROJECT
*
* Id = EE, EGI, etc
* */
public String subscriber(String type, String id) {
return type.toUpperCase() + "_" + id.toUpperCase();
}
}

View File

@ -0,0 +1,25 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import eu.dnetlib.uoaauthorizationlibrary.configuration.SecurityConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {
SecurityConfig securityConfig;
@Autowired
CorsConfig(SecurityConfig securityConfig) {
this.securityConfig = securityConfig;
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.allowCredentials(true);
}
}

View File

@ -0,0 +1,52 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import eu.dnetlib.uoaauthorizationlibrary.utils.AuthorizationUtils;
import eu.dnetlib.uoaauthorizationlibrary.utils.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private AuthorizationUtils utils;
@Autowired
public UserDetailsServiceImpl(AuthorizationUtils utils) {
this.utils = utils;
}
private List<GrantedAuthority> getAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<>();
for(String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
@Override
public UserDetails loadUserByUsername(String token) throws UsernameNotFoundException {
final UserInfo user = utils.getUserInfo(token);
if (user == null) {
throw new UsernameNotFoundException("invalid token: " + token);
}
return org.springframework.security.core.userdetails.User
.withUsername(user.getEmail()).password("")
.authorities(getAuthorities(user.getRoles()))
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false)
.build();
}
}

View File

@ -0,0 +1,34 @@
package eu.dnetlib.uoaauthorizationlibrary.security;
import eu.dnetlib.uoaauthorizationlibrary.utils.AuthorizationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableWebSecurity
@ComponentScan(basePackages = {"eu.dnetlib.uoaauthorizationlibrary.*"})
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private AuthorizationProvider authorizationProvider;
private AuthorizationUtils utils;
@Autowired
WebSecurityConfig(AuthorizationProvider authorizationProvider, AuthorizationUtils utils) {
this.authorizationProvider = authorizationProvider;
this.utils = utils;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.apply(new AuthorizationFilterConfigurer(authorizationProvider, utils));
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().anyRequest().authenticated();
http.httpBasic();
}
}

View File

@ -0,0 +1,117 @@
package eu.dnetlib.uoaauthorizationlibrary.utils;
import com.google.gson.Gson;
import eu.dnetlib.uoaauthorizationlibrary.configuration.SecurityConfig;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
@Component
public class AuthorizationUtils {
private final Logger log = Logger.getLogger(this.getClass());
private SecurityConfig securityConfig;
@Autowired
AuthorizationUtils(SecurityConfig securityConfig) {
this.securityConfig = securityConfig;
}
public String getToken(HttpServletRequest request) {
return this.getCookie(request, "AccessToken");
}
public boolean checkCookies(HttpServletRequest request) {
boolean valid = true;
String cookieValue = this.getCookie(request, "AccessToken");
if (cookieValue == null || cookieValue.isEmpty()) {
log.info("no cookie available ");
valid = false;
}
return valid;
}
private String getCookie(HttpServletRequest request, String cookieName) {
if (request.getCookies() == null) {
return null;
}
for (Cookie c : request.getCookies()) {
if (c.getName().equals(cookieName)) {
return c.getValue();
}
}
return null;
}
public UserInfo getUserInfo(String accessToken) {
String url = securityConfig.getUserInfoUrl() + accessToken;
try {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
if (con.getResponseCode() != 200) {
log.debug("User info response code is: " + con.getResponseCode());
return null;
}
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuilder response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine).append("\n");
}
in.close();
return json2UserInfo(response.toString());
} catch (Exception e) {
log.error("An error occured while trying to fetch user info ", e);
return null;
}
}
private UserInfo json2UserInfo(String json) {
if (json == null) {
return null;
}
BufferedReader br = new BufferedReader(new StringReader(json));
//convert the json string back to object
Gson gson = new Gson();
UserInfo userInfo = null;
try {
userInfo = gson.fromJson(br, UserInfo.class);
} catch (Exception e) {
log.debug("Error in parsing json response. Given json is : " + json, e);
return null;
}
try {
if (userInfo != null && userInfo.getEdu_person_entitlements() != null) {
for (int i = 0; i < userInfo.getEdu_person_entitlements().size(); i++) {
String role = userInfo.getEdu_person_entitlements().get(i);
role = role.split(":")[role.split(":").length - 1];
role = role.replace("+", " ");
userInfo.getEdu_person_entitlements().set(i, role);
}
}
} catch (Exception e) {
log.debug("Error in parsing Edu_person_entitlements : ", e);
return null;
}
return userInfo;
}
public boolean isAuthorized(String token) {
UserInfo userInfo = getUserInfo(token);
if (userInfo != null) {
return true;
} else {
log.debug(" User has no Valid UserInfo");
return false;
}
}
}

View File

@ -0,0 +1,53 @@
package eu.dnetlib.uoaauthorizationlibrary.utils;
import java.util.ArrayList;
import java.util.List;
public class UserInfo {
String name;
String email;
List<String> edu_person_entitlements = new ArrayList<String>();
List<String> roles = new ArrayList<>();
@Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", email='" + email + '\'' +
", edu_person_entitlements=" + edu_person_entitlements +
", roles=" + roles +
'}';
}
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 List<String> getEdu_person_entitlements() {
return edu_person_entitlements;
}
public void setEdu_person_entitlements(List<String> edu_person_entitlements) {
this.edu_person_entitlements = edu_person_entitlements;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}

View File

@ -0,0 +1 @@
server.port=9090

View File

@ -0,0 +1,12 @@
#dev
authorization.security.userInfoUrl = http://mpagasas.di.uoa.gr:8080/dnet-openaire-users-1.0.0-SNAPSHOT/api/users/getUserInfo?accessToken=
authorization.security.originServer = *
#beta
#authorization.security.userInfoUrl = https://beta.services.openaire.eu/uoa-user-management/api/users/getUserInfo?accessToken=
#authorization.security.originServer = .openaire.eu
#production
#authorization.security.userInfoUrl = https://services.openaire.eu/uoa-user-management/api/users/getUserInfo?accessToken=
#authorization.security.originServer = .openaire.eu

View File

@ -0,0 +1,20 @@
log4j.rootLogger = DEBUG, R
log4j.logger.eu.dnetlib = DEBUG
log4j.logger.org.springframework = DEBUG, S
log4j.additivity.org.springframework = false
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=/var/log/dnet/uoa-authorization-library/uoa-authorization-library.log
log4j.appender.R.MaxFileSize=10MB
log4j.appender.R.MaxBackupIndex=10
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern= %d %p %t [%c] - %m%n
log4j.appender.S=org.apache.log4j.RollingFileAppender
log4j.appender.S.File=/var/log/dnet/uoa-authorization-library/uoa-authorization-library-spring.log
log4j.appender.S.MaxFileSize=10MB
log4j.appender.S.MaxBackupIndex=10
log4j.appender.S.layout=org.apache.log4j.PatternLayout
log4j.appender.S.layout.ConversionPattern= %d %p %t [%c] - %m%n