DS-3542 expiration time configurable

This commit is contained in:
frederic
2017-10-23 11:21:58 +02:00
committed by Tom Desair
parent bc055f1612
commit ec0be65d34
9 changed files with 43 additions and 45 deletions

View File

@@ -1,2 +1 @@
-- This adds an extra column to the eperson table where we save a salt for stateless authentication alter table eperson add session_salt varchar(16);
ALTER TABLE eperson ADD session_salt varchar(16);

View File

@@ -18,24 +18,30 @@ import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/")
public class StatusRestController { public class StatusRestController {
@RequestMapping(value = "/status", method = RequestMethod.GET) @RequestMapping(value="/status", method = RequestMethod.GET)
public Status status(HttpServletRequest request, HttpServletResponse response) throws SQLException { public String status(HttpServletRequest request, HttpServletResponse response) throws SQLException {
Context context = ContextUtil.obtainContext(request); Context context = ContextUtil.obtainContext(request);
//context.getDBConnection().setAutoCommit(false); // Disable autocommit. //context.getDBConnection().setAutoCommit(false); // Disable autocommit.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null && !(authentication.getPrincipal().equals("anonymousUser"))) {
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) authentication.getAuthorities();
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) authentication.getAuthorities(); context.setCurrentUser(EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, authentication.getName()));
context.setCurrentUser(EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, authentication.getName()));
EPerson current = context.getCurrentUser();
return new Status(current);
EPerson current = context.getCurrentUser();
String status = "EPerson: " + current.getEmail() + "\nFull name: " + current.getFullName() + "\nAuthorities: \n";
for (SimpleGrantedAuthority authority: authorities) {
status += authority.getAuthority();
}
return status;
} else {
return "Not authenticated";
}
} }
} }

View File

@@ -25,14 +25,13 @@ public class CustomLogoutHandler implements LogoutHandler {
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) { public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
Cookie cookie = WebUtils.getCookie(httpServletRequest,"access_token"); Cookie cookie = WebUtils.getCookie(httpServletRequest,"access_token");
EPerson ePerson = tokenAuthenticationService.getAuthentication(cookie.getValue(), httpServletRequest);
Context context = null; Context context = null;
try { try {
context = ContextUtil.obtainContext(httpServletRequest); context = ContextUtil.obtainContext(httpServletRequest);
} catch (SQLException e) { } catch (SQLException e) {
log.error("Unable to obtain context", e); log.error("Unable to obtain context", e);
} }
EPerson ePerson = tokenAuthenticationService.getAuthentication(cookie.getValue(), httpServletRequest, context);
ePerson.setSessionSalt(""); ePerson.setSessionSalt("");
try { try {
context.commit(); context.commit();

View File

@@ -37,16 +37,17 @@ public class EPersonRestAuthenticationProvider implements AuthenticationProvider
context = new Context(); context = new Context();
String name = authentication.getName(); String name = authentication.getName();
String password = authentication.getCredentials().toString(); String password = authentication.getCredentials().toString();
HttpServletRequest httpServletRequest = request;
List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
int implicitStatus = authenticationService.authenticateImplicit(context, null, null, null, request); int implicitStatus = authenticationService.authenticateImplicit(context, null, null, null, httpServletRequest);
if (implicitStatus == AuthenticationMethod.SUCCESS) { if (implicitStatus == AuthenticationMethod.SUCCESS) {
log.info(LogManager.getHeader(context, "login", "type=implicit")); log.info(LogManager.getHeader(context, "login", "type=implicit"));
return new DSpaceAuthentication(name, password, grantedAuthorities); return new DSpaceAuthentication(name, password, grantedAuthorities);
} else { } else {
int authenticateResult = authenticationService.authenticate(context, name, password, null, request); int authenticateResult = authenticationService.authenticate(context, name, password, null, httpServletRequest);
if (AuthenticationMethod.SUCCESS == authenticateResult) { if (AuthenticationMethod.SUCCESS == authenticateResult) {
log.info(LogManager log.info(LogManager

View File

@@ -10,7 +10,6 @@ import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.keygen.KeyGenerators; import org.springframework.security.crypto.keygen.KeyGenerators;
@@ -29,22 +28,20 @@ public class JWTTokenHandler {
private static final Logger log = LoggerFactory.getLogger(JWTTokenHandler.class); private static final Logger log = LoggerFactory.getLogger(JWTTokenHandler.class);
//TODO configurable through config files //TODO configurable through config files
private String jwtKey; private static String jwtKey = "thisisatestsecretkeyforjwttokens";
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
public static final String EPERSON_ID = "eid"; public static final String EPERSON_ID = "eid";
public static final String SPECIAL_GROUPS = "sg"; public static final String SPECIAL_GROUPS = "sg";
public EPerson parseEPersonFromToken(String token, HttpServletRequest request) throws JOSEException, ParseException, SQLException {
public JWTTokenHandler() {
jwtKey = DSpaceServicesFactory.getInstance().getConfigurationService().getProperty("jwt.secret", "defaultjwtkeysecret");
System.out.println(jwtKey);
}
public EPerson parseEPersonFromToken(String token, HttpServletRequest request, Context context) throws JOSEException, ParseException, SQLException {
SignedJWT signedJWT = SignedJWT.parse(token); SignedJWT signedJWT = SignedJWT.parse(token);
Context context = new Context();
EPerson ePerson = ePersonService.find(context, UUID.fromString(signedJWT.getJWTClaimsSet().getClaim(EPERSON_ID).toString())); EPerson ePerson = ePersonService.find(context, UUID.fromString(signedJWT.getJWTClaimsSet().getClaim(EPERSON_ID).toString()));
String ipAddress = getIpAddress(request); String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
JWSVerifier verifier = new MACVerifier(jwtKey + ePerson.getSessionSalt() + ipAddress); JWSVerifier verifier = new MACVerifier(jwtKey + ePerson.getSessionSalt() + ipAddress);
//If token is valid and not expired return eperson in token //If token is valid and not expired return eperson in token
@@ -61,7 +58,10 @@ public class JWTTokenHandler {
public String createTokenForEPerson(Context context, HttpServletRequest request, EPerson ePerson, List<Group> groups) throws JOSEException { public String createTokenForEPerson(Context context, HttpServletRequest request, EPerson ePerson, List<Group> groups) throws JOSEException {
StringKeyGenerator stringKeyGenerator = KeyGenerators.string(); StringKeyGenerator stringKeyGenerator = KeyGenerators.string();
String salt = stringKeyGenerator.generateKey(); String salt = stringKeyGenerator.generateKey();
String ipAddress = getIpAddress(request); String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
JWSSigner signer = new MACSigner(jwtKey + salt + ipAddress); JWSSigner signer = new MACSigner(jwtKey + salt + ipAddress);
List<String> groupIds = groups.stream().map(group -> group.getID().toString()).collect(Collectors.toList()); List<String> groupIds = groups.stream().map(group -> group.getID().toString()).collect(Collectors.toList());
@@ -87,13 +87,4 @@ public class JWTTokenHandler {
return signedJWT.serialize(); return signedJWT.serialize();
} }
public String getIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
}
} }

View File

@@ -67,7 +67,7 @@ public class StatelessAuthenticationFilter extends BasicAuthenticationFilter{
} catch (SQLException e) { } catch (SQLException e) {
log.error("Unable to obtain context from request", e); log.error("Unable to obtain context from request", e);
} }
EPerson eperson = tokenAuthenticationService.getAuthentication(token, request, context); EPerson eperson = tokenAuthenticationService.getAuthentication(token, request);
boolean isAdmin = false; boolean isAdmin = false;
try { try {
isAdmin = authorizeService.isAdmin(context, eperson); isAdmin = authorizeService.isAdmin(context, eperson);

View File

@@ -36,8 +36,8 @@ public class TokenAuthenticationService {
public void addAuthentication(HttpServletRequest request, HttpServletResponse response, String email) { public void addAuthentication(HttpServletRequest request, HttpServletResponse response, String email) {
try { try {
EPerson ePerson = ePersonService.findByEmail(ContextUtil.obtainContext(request), email);
Context context = ContextUtil.obtainContext(request); Context context = ContextUtil.obtainContext(request);
EPerson ePerson = ePersonService.findByEmail(context, email);
List<Group> groups = authenticationService.getSpecialGroups(context, request); List<Group> groups = authenticationService.getSpecialGroups(context, request);
String token = jwtTokenHandler.createTokenForEPerson(context, request, ePerson, groups); String token = jwtTokenHandler.createTokenForEPerson(context, request, ePerson, groups);
//TODO token is saved in a cookie, but might be better to save it in http header //TODO token is saved in a cookie, but might be better to save it in http header
@@ -51,9 +51,9 @@ public class TokenAuthenticationService {
} }
public EPerson getAuthentication(String token, HttpServletRequest request, Context context) { public EPerson getAuthentication(String token, HttpServletRequest request) {
try { try {
EPerson ePerson = jwtTokenHandler.parseEPersonFromToken(token, request, context); EPerson ePerson = jwtTokenHandler.parseEPersonFromToken(token, request);
return ePerson; return ePerson;
} catch (JOSEException e) { } catch (JOSEException e) {
log.error("Jose error", e); log.error("Jose error", e);

View File

@@ -11,7 +11,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@@ -29,6 +28,8 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.headers().cacheControl(); http.headers().cacheControl();
http http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling().and() .exceptionHandling().and()
@@ -36,16 +37,16 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
.servletApi().and() .servletApi().and()
.csrf().disable() .csrf().disable()
.logout().addLogoutHandler(customLogoutHandler).logoutRequestMatcher(new AntPathRequestMatcher("/api/logout")).logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()).permitAll() .logout().addLogoutHandler(customLogoutHandler).logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/status").permitAll()
.and() .and()
.authorizeRequests() .authorizeRequests()
.antMatchers( "/api/login").permitAll() .antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.GET, "/api/status").permitAll() .antMatchers(HttpMethod.GET, "/status").permitAll()
.and() .and()
.addFilterBefore(new StatelessLoginFilter("/api/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(new StatelessLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
// Custom Token based authentication based on the header previously given to the client // Custom Token based authentication based on the header previously given to the client
.addFilterBefore(new StatelessAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class); .addFilterBefore(new StatelessAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);

View File

@@ -50,4 +50,5 @@
plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.PasswordAuthentication plugin.sequence.org.dspace.authenticate.AuthenticationMethod = org.dspace.authenticate.PasswordAuthentication
jwt.secret = thisisatestsecretkeyforjwttokens jwt.token.secret = thisisatestsecretkeyforjwttokens
jwt.token.expiration = 1800000