diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..afe97e7 --- /dev/null +++ b/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + eu.dnetlib + uoa-authorization-library + 1.0.0-SNAPSHOT + jar + + uoa-authorization-library + + + org.springframework.boot + spring-boot-starter-parent + 1.5.8.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-security + + + com.google.code.gson + gson + 2.8.2 + + + log4j + log4j + 1.2.17 + + + + + + uoa-authorization-library + + diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/configuration/AuthorizationConfiguration.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/configuration/AuthorizationConfiguration.java new file mode 100644 index 0000000..a562764 --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/configuration/AuthorizationConfiguration.java @@ -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 { } diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/configuration/SecurityConfig.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/configuration/SecurityConfig.java new file mode 100644 index 0000000..42ddb7d --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/configuration/SecurityConfig.java @@ -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; + } + +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationFilter.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationFilter.java new file mode 100644 index 0000000..dc14d08 --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationFilter.java @@ -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() { + + } +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationFilterConfigurer.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationFilterConfigurer.java new file mode 100644 index 0000000..bd67a84 --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationFilterConfigurer.java @@ -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 { + + 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); + } + +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationProvider.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationProvider.java new file mode 100644 index 0000000..acba1ba --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationProvider.java @@ -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()); + } +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationService.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationService.java new file mode 100644 index 0000000..92b3d49 --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/AuthorizationService.java @@ -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(); + } +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/CorsConfig.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/CorsConfig.java new file mode 100644 index 0000000..2291af2 --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/CorsConfig.java @@ -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); + } +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/UserDetailsServiceImpl.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/UserDetailsServiceImpl.java new file mode 100644 index 0000000..634a577 --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/UserDetailsServiceImpl.java @@ -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 getAuthorities(List roles) { + List 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(); + } + +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/WebSecurityConfig.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/WebSecurityConfig.java new file mode 100644 index 0000000..cbb8f3d --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/security/WebSecurityConfig.java @@ -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(); + } + +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/utils/AuthorizationUtils.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/utils/AuthorizationUtils.java new file mode 100644 index 0000000..9ede1fe --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/utils/AuthorizationUtils.java @@ -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; + } + + } +} diff --git a/src/main/java/eu/dnetlib/uoaauthorizationlibrary/utils/UserInfo.java b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/utils/UserInfo.java new file mode 100644 index 0000000..939813f --- /dev/null +++ b/src/main/java/eu/dnetlib/uoaauthorizationlibrary/utils/UserInfo.java @@ -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 edu_person_entitlements = new ArrayList(); + List 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 getEdu_person_entitlements() { + return edu_person_entitlements; + } + + public void setEdu_person_entitlements(List edu_person_entitlements) { + this.edu_person_entitlements = edu_person_entitlements; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..5ff0285 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=9090 \ No newline at end of file diff --git a/src/main/resources/authorization.properties b/src/main/resources/authorization.properties new file mode 100644 index 0000000..45a28ec --- /dev/null +++ b/src/main/resources/authorization.properties @@ -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 diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties new file mode 100644 index 0000000..836701b --- /dev/null +++ b/src/main/resources/log4j.properties @@ -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 \ No newline at end of file