diff --git a/assets/config/opensrp.properties b/assets/config/opensrp.properties index 7eccfece2c..76be2b4fd1 100644 --- a/assets/config/opensrp.properties +++ b/assets/config/opensrp.properties @@ -90,3 +90,12 @@ opensrp.site.url="" #search for missing clients opensrp.sync.search.missing.client=false + +#duration in seconds to cache authetication time to live +opensrp.authencation.cache.ttl=600 + +#redis settings +redis.host=localhost +redis.port=6379 +redis.password=RedI$P@S5 +redis.pool.max.connections=25 \ No newline at end of file diff --git a/opensrp-core/src/main/resources/applicationContext-opensrp.xml b/opensrp-core/src/main/resources/applicationContext-opensrp.xml index bad0b47046..538214525d 100644 --- a/opensrp-core/src/main/resources/applicationContext-opensrp.xml +++ b/opensrp-core/src/main/resources/applicationContext-opensrp.xml @@ -2,12 +2,13 @@ - + @@ -22,11 +23,57 @@ - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opensrp-core/src/main/resources/persistence_postgres.xml b/opensrp-core/src/main/resources/persistence_postgres.xml index 62ecde7657..621a3abaad 100644 --- a/opensrp-core/src/main/resources/persistence_postgres.xml +++ b/opensrp-core/src/main/resources/persistence_postgres.xml @@ -47,4 +47,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/opensrp-web/pom.xml b/opensrp-web/pom.xml index 0a7848ee5d..b34ef5605e 100644 --- a/opensrp-web/pom.xml +++ b/opensrp-web/pom.xml @@ -22,12 +22,55 @@ 4.2.8.Final - + + + jedis + + true + + opensrp.redis.client.type + jedis + + + + jedis + + + + + redis.clients + jedis + 2.4.1 + + + + + lettuce + + + opensrp.redis.client.type + lettuce + + + + lettuce + + + + + com.lambdaworks + lettuce + 2.3.3 + + + + opensrp src/main/resources + true ../assets/config @@ -55,6 +98,23 @@ + + org.apache.maven.plugins + maven-war-plugin + 2.5 + + + + true + src/main/webapp + + **/web.xml + + + + true + + org.mortbay.jetty jetty-maven-plugin @@ -278,9 +338,12 @@ 3.1 --> - - - - + + + org.springframework.data + spring-data-redis + 1.3.6.RELEASE + + diff --git a/opensrp-web/src/main/java/org/opensrp/web/controller/MultimediaController.java b/opensrp-web/src/main/java/org/opensrp/web/controller/MultimediaController.java index d2b5d1fb61..0bd7f6c948 100644 --- a/opensrp-web/src/main/java/org/opensrp/web/controller/MultimediaController.java +++ b/opensrp-web/src/main/java/org/opensrp/web/controller/MultimediaController.java @@ -11,6 +11,7 @@ import java.net.URLConnection; import java.nio.charset.Charset; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.opensrp.domain.Multimedia; @@ -26,6 +27,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Controller; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.PathVariable; @@ -68,11 +71,11 @@ public class MultimediaController { @RequestMapping(value = "/download/{fileName:.+}", method = RequestMethod.GET) public void downloadFile(HttpServletResponse response, @PathVariable("fileName") String fileName, @RequestHeader(value = "username") String userName, - @RequestHeader(value = "password") String password) + @RequestHeader(value = "password") String password, HttpServletRequest request) throws Exception { try { - if (authenticate(userName, password).isAuthenticated()) { + if (authenticate(userName, password, request).isAuthenticated()) { File file = new File(multiMediaDir + File.separator + "images" + File.separator + fileName); if (fileName.endsWith("mp4")) { file = new File(multiMediaDir + File.separator + "videos" + File.separator + fileName); @@ -99,11 +102,11 @@ public void downloadFile(HttpServletResponse response, @PathVariable("fileName") @RequestMapping(value = "/profileimage/{baseEntityId}", method = RequestMethod.GET) public void downloadFileByClientId(HttpServletResponse response, @PathVariable("baseEntityId") String baseEntityId, @RequestHeader(value = "username") String userName, - @RequestHeader(value = "password") String password) + @RequestHeader(value = "password") String password, HttpServletRequest request) throws Exception { try { - if (authenticate(userName, password).isAuthenticated()) { + if (authenticate(userName, password, request).isAuthenticated()) { Multimedia multiMedia = multimediaService.findByCaseId(baseEntityId); if (multiMedia == null || multiMedia.getFilePath() == null) { @@ -148,10 +151,11 @@ public ResponseEntity uploadFiles(@RequestParam("anm-id") String provide return new ResponseEntity<>(new Gson().toJson(status), HttpStatus.OK); } - private Authentication authenticate(String userName, String password) { - Authentication auth = new UsernamePasswordAuthenticationToken(userName, password); - auth = provider.authenticate(auth); - return auth; + private Authentication authenticate(String userName, String password, HttpServletRequest request) { + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userName, password); + WebAuthenticationDetails details = new WebAuthenticationDetailsSource().buildDetails(request); + auth.setDetails(details); + return provider.authenticate(auth); } private void downloadFile(File file, HttpServletResponse response) throws Exception { diff --git a/opensrp-web/src/main/java/org/opensrp/web/security/DrishtiAuthenticationProvider.java b/opensrp-web/src/main/java/org/opensrp/web/security/DrishtiAuthenticationProvider.java index 2548d23a0d..64ba66c57a 100644 --- a/opensrp-web/src/main/java/org/opensrp/web/security/DrishtiAuthenticationProvider.java +++ b/opensrp-web/src/main/java/org/opensrp/web/security/DrishtiAuthenticationProvider.java @@ -3,14 +3,19 @@ import static java.text.MessageFormat.format; import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; -import org.json.JSONException; import org.opensrp.api.domain.User; import org.opensrp.connector.openmrs.service.OpenmrsUserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -18,6 +23,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.stereotype.Component; import ch.lambdaj.Lambda; @@ -25,70 +31,103 @@ @Component public class DrishtiAuthenticationProvider implements AuthenticationProvider { - private static Logger logger = LoggerFactory.getLogger(DrishtiAuthenticationProvider.class.toString()); - public static final String USER_NOT_FOUND = "The username or password you entered is incorrect. Please enter the correct credentials."; - public static final String USER_NOT_ACTIVATED = "The user has been registered but not activated. Please contact your local administrator."; - public static final String INTERNAL_ERROR = "Failed to authenticate user due to internal server error."; - - //private AllOpenSRPUsers allOpenSRPUsers; - private PasswordEncoder passwordEncoder; - private OpenmrsUserService openmrsUserService; - - - @Autowired - public DrishtiAuthenticationProvider(OpenmrsUserService openmrsUserService, @Qualifier("shaPasswordEncoder") PasswordEncoder passwordEncoder) { - this.openmrsUserService = openmrsUserService; - this.passwordEncoder = passwordEncoder; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - User user = getDrishtiUser(authentication, authentication.getName()); - // get user after authentication - if (user == null) { - throw new BadCredentialsException(USER_NOT_FOUND); - } - - if (user.getVoided() != null && user.getVoided()) { - throw new BadCredentialsException(USER_NOT_ACTIVATED); - } - - return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), getRolesAsAuthorities(user)); - } - - @Override - public boolean supports(Class authentication) { - return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication) - && authentication.equals(UsernamePasswordAuthenticationToken.class); - } - - private List getRolesAsAuthorities(User user) { - return Lambda.convert(user.getRoles(), new Converter() { - @Override - public SimpleGrantedAuthority convert(String role) { - return new SimpleGrantedAuthority("ROLE_OPENMRS"); - } - }); - } - - - - public User getDrishtiUser(Authentication authentication, String username) { - User user = null; - try { - if(openmrsUserService.authenticate(authentication.getName(), authentication.getCredentials().toString())){ - boolean response = openmrsUserService.deleteSession(authentication.getName(),authentication.getCredentials().toString()); - user = openmrsUserService.getUser(username); - if(!response){ - logger.error(format("{0}. Exception: {1}", INTERNAL_ERROR, "Unable to clear session")); - - } + + private static Logger logger = LoggerFactory.getLogger(DrishtiAuthenticationProvider.class.toString()); + + public static final String USER_NOT_FOUND = "The username or password you entered is incorrect. Please enter the correct credentials."; + + public static final String USER_NOT_ACTIVATED = "The user has been registered but not activated. Please contact your local administrator."; + + public static final String INTERNAL_ERROR = "Failed to authenticate user due to internal server error."; + + private static final String AUTH_HASH_KEY = "_auth"; + + //private AllOpenSRPUsers allOpenSRPUsers; + private PasswordEncoder passwordEncoder; + + private OpenmrsUserService openmrsUserService; + + @Resource(name = "redisTemplate") + private HashOperations hashOps; + + @Autowired + private RedisTemplate redisTemplate; + + @Value("#{opensrp['opensrp.authencation.cache.ttl']}") + private int cacheTTL; + + @Autowired + public DrishtiAuthenticationProvider(OpenmrsUserService openmrsUserService, + @Qualifier("shaPasswordEncoder") PasswordEncoder passwordEncoder) { + this.openmrsUserService = openmrsUserService; + this.passwordEncoder = passwordEncoder; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String userAddress = ((WebAuthenticationDetails) authentication.getDetails()).getRemoteAddress(); + String key = userAddress + authentication.getName(); + if (hashOps.hasKey(key, AUTH_HASH_KEY)) { + Authentication auth = hashOps.get(key, AUTH_HASH_KEY); + //if credentials is same as cached returned cached else eject cached authentication + if (auth.getCredentials().equals(authentication.getCredentials())) + return auth; + else + hashOps.delete(key, AUTH_HASH_KEY); + + } + User user = getDrishtiUser(authentication, authentication.getName()); + // get user after authentication + if (user == null) { + throw new BadCredentialsException(USER_NOT_FOUND); + } + + if (user.getVoided() != null && user.getVoided()) { + throw new BadCredentialsException(USER_NOT_ACTIVATED); + } + + Authentication auth = new UsernamePasswordAuthenticationToken(authentication.getName(), + authentication.getCredentials(), getRolesAsAuthorities(user)); + hashOps.put(key, AUTH_HASH_KEY, auth); + redisTemplate.expire(key, cacheTTL, TimeUnit.SECONDS); + return auth; + + } + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication) + && authentication.equals(UsernamePasswordAuthenticationToken.class); + } + + private List getRolesAsAuthorities(User user) { + return Lambda.convert(user.getRoles(), new Converter() { + + @Override + public SimpleGrantedAuthority convert(String role) { + return new SimpleGrantedAuthority("ROLE_OPENMRS"); + } + }); + } + + public User getDrishtiUser(Authentication authentication, String username) { + User user = null; + try { + if (openmrsUserService.authenticate(authentication.getName(), authentication.getCredentials().toString())) { + boolean response = openmrsUserService.deleteSession(authentication.getName(), + authentication.getCredentials().toString()); + user = openmrsUserService.getUser(username); + if (!response) { + logger.error(format("{0}. Exception: {1}", INTERNAL_ERROR, "Unable to clear session")); + + } } - } catch (Exception e) { - logger.error(format("{0}. Exception: {1}", INTERNAL_ERROR, e)); - e.printStackTrace(); - throw new BadCredentialsException(INTERNAL_ERROR); - } - return user; - } + } + catch (Exception e) { + logger.error(format("{0}. Exception: {1}", INTERNAL_ERROR, e)); + e.printStackTrace(); + throw new BadCredentialsException(INTERNAL_ERROR); + } + return user; + } } diff --git a/opensrp-web/src/main/webapp/WEB-INF/web.xml b/opensrp-web/src/main/webapp/WEB-INF/web.xml index 8cd616cdcb..d9951847a5 100644 --- a/opensrp-web/src/main/webapp/WEB-INF/web.xml +++ b/opensrp-web/src/main/webapp/WEB-INF/web.xml @@ -77,6 +77,11 @@ org.springframework.web.context.ContextLoaderListener + + spring.profiles.active + ${opensrp.database.profile},${opensrp.redis.client.profile} + + opensrp diff --git a/pom.xml b/pom.xml index 8dc95f796a..26cd2b48c8 100644 --- a/pom.xml +++ b/pom.xml @@ -1,424 +1,449 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - org.opensrp - opensrp - pom - 0.1-SNAPSHOT - opensrp + org.opensrp + opensrp + pom + 0.1-SNAPSHOT + opensrp http://github.com/OpenSRP/opensrp-server - - opensrp-common - opensrp-api - opensrp-form - opensrp-core - opensrp-interface - opensrp-register - opensrp-connector - opensrp-web - - + + opensrp-common + opensrp-api + opensrp-form + opensrp-core + opensrp-interface + opensrp-register + opensrp-connector + opensrp-web + + - - ${project.basedir} + + ${project.basedir} 0.11 org.motechproject.contrib-drishti 0.1-SNAPSHOT UTF-8 3.1.1.RELEASE - 5.1.36 + 5.1.36 1.9.7 - always - + always + - - - opensrp.nexus - OpenSRP Releases Repository - http://ci.smartregistries.org:8081/nexus/content/repositories/releases - false - - - opensrp.nexus - OpenSRP Snapshots Repository - http://ci.smartregistries.org:8081/nexus/content/repositories/opensrp-server - false - - + + + opensrp.nexus + OpenSRP Releases Repository + http://ci.smartregistries.org:8081/nexus/content/repositories/releases + false + + + opensrp.nexus + OpenSRP Snapshots Repository + http://ci.smartregistries.org:8081/nexus/content/repositories/opensrp-server + false + + - - - jasmine-js-bdd - Jasmine JavaScript BDD Repository - http://searls-maven-repository.googlecode.com/svn/trunk/snapshots - - + + + jasmine-js-bdd + Jasmine JavaScript BDD Repository + http://searls-maven-repository.googlecode.com/svn/trunk/snapshots + + - - - central - http://repo1.maven.org/maven2 - Repository for dependencies - - true - - - - spring-maven-release - Spring Maven Release Repository - http://maven.springframework.org/release - - - spring-maven-milestone - Spring Maven Milestone Repository - http://maven.springframework.org/milestone - - - motech.opensrp - Motech Snapshots Repository - http://nexus.motechproject.org/content/repositories/opensrp-server - - true - ${opensrp.updatePolicy} - - - - motech.nexus - Motech Snapshots Repository - http://nexus.motechproject.org/content/repositories/snapshots - - true - ${opensrp.updatePolicy} - - - - motech.nexus.release - Motech Snapshots Repository - http://nexus.motechproject.org/content/repositories/releases - - - - - skip-integration-tests - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.9 - - - - integration-test - verify - - - -XX:MaxPermSize=1024M - - **/it/** - - true - - - - - - - - - integration-tests - - true - - - - - - - maven-antrun-plugin - 1.6 - - - Drop DB Before Unit and Repository Tests - process-test-classes - - - - - - - - - - - - - - - - - run - - - - CREATE DB OPENSRP - pre-integration-test - - - - - - - - - - - - - run - - - - - - org.codehaus.mojo - sql-maven-plugin - 1.5 - - - - mysql - mysql-connector-java - ${mysql.connector.version} - - + + + central + http://repo1.maven.org/maven2 + Repository for dependencies + + true + + + + spring-maven-release + Spring Maven Release Repository + http://maven.springframework.org/release + + + spring-maven-milestone + Spring Maven Milestone Repository + http://maven.springframework.org/milestone + + + motech.opensrp + Motech Snapshots Repository + http://nexus.motechproject.org/content/repositories/opensrp-server + + true + ${opensrp.updatePolicy} + + + + motech.nexus + Motech Snapshots Repository + http://nexus.motechproject.org/content/repositories/snapshots + + true + ${opensrp.updatePolicy} + + + + motech.nexus.release + Motech Snapshots Repository + http://nexus.motechproject.org/content/repositories/releases + + + + + skip-integration-tests + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.9 + + + + integration-test + verify + + + -XX:MaxPermSize=1024M + + **/it/** + + true + + + + + + + + + integration-tests + + true + - - ${jdbc.driverClassName} - ${jdbc.url-wo-db} - ${jdbc.username} - ${jdbc.password} - ${maven.test.skip} - + + + + + maven-antrun-plugin + 1.6 + + + Drop DB Before Unit and Repository Tests + process-test-classes + + + + + + + + + + + + + + + + + run + + + + CREATE DB OPENSRP + pre-integration-test + + + + + + + + + + + + + run + + + + + + org.codehaus.mojo + sql-maven-plugin + 1.5 + + + + mysql + mysql-connector-java + ${mysql.connector.version} + + - - - - drop-create-relational-db - pre-integration-test - - execute - - - ${jdbc.url-wo-db} - ${jdbc.username} - ${jdbc.password} - true - - drop database if exists ${db.quartz}; - create database ${db.quartz}; - - abort - - - - - create-quartz-tables - pre-integration-test - - execute - - - ${jdbc.url-wo-db}/${db.quartz} - ${jdbc.username} - ${jdbc.password} - true - - ../build/sql/tables_quartz_${jdbc.backend}.sql - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.9 - - - - integration-test - verify - - - -XX:MaxPermSize=1024M - - **/it/** - - true - - - - - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.5.1 - - 1.7 - 1.7 - UTF-8 - - - - org.apache.maven.plugins - maven-resources-plugin - 2.5 - - UTF-8 - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.5 - + + ${jdbc.driverClassName} + ${jdbc.url-wo-db} + ${jdbc.username} + ${jdbc.password} + ${maven.test.skip} + - - org.codehaus.mojo - properties-maven-plugin - 1.0-alpha-2 - - - initialize - - read-project-properties - - - - ${main.basedir}/build/maven.properties - ${main.basedir}/assets/config/opensrp.properties - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.20 - - true - - **/it/** - - - - - - - org.jacoco - jacoco-maven-plugin - 0.7.9 - - ${basedir}/target/coverage-reports/jacoco-unit.exec - ${basedir}/target/coverage-reports/jacoco-unit.exec - - - - jacoco-initialize - - prepare-agent - - - - jacoco-site - test - - report - - - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.0.0 - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-antrun-plugin - - - [1.6,) - - - run - - - - - - - - - - org.codehaus.mojo - - - properties-maven-plugin - - - [1.0-alpha-2,) - - - - read-project-properties - - - - - - - - - - - - - - + + + + drop-create-relational-db + pre-integration-test + + execute + + + ${jdbc.url-wo-db} + ${jdbc.username} + ${jdbc.password} + true + + drop database if exists ${db.quartz}; + create database ${db.quartz}; + + abort + + + + + create-quartz-tables + pre-integration-test + + execute + + + ${jdbc.url-wo-db}/${db.quartz} + ${jdbc.username} + ${jdbc.password} + true + + ../build/sql/tables_quartz_${jdbc.backend}.sql + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.9 + + + + integration-test + verify + + + -XX:MaxPermSize=1024M + + **/it/** + + true + + + + + + + + + + couchDb + + + opensrp.database.type + couchdb + + + + couchdb + + + + postgres + + true + + opensrp.database.type + postgres + + + + postgres + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.7 + 1.7 + UTF-8 + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + UTF-8 + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.5 + + + + org.codehaus.mojo + properties-maven-plugin + 1.0-alpha-2 + + + initialize + + read-project-properties + + + + ${main.basedir}/build/maven.properties + ${main.basedir}/assets/config/opensrp.properties + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.20 + + true + + **/it/** + + + + + + + org.jacoco + jacoco-maven-plugin + 0.7.9 + + ${basedir}/target/coverage-reports/jacoco-unit.exec + ${basedir}/target/coverage-reports/jacoco-unit.exec + + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.0.0 + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-antrun-plugin + + + [1.6,) + + + run + + + + + + + + + + org.codehaus.mojo + + + properties-maven-plugin + + + [1.0-alpha-2,) + + + + read-project-properties + + + + + + + + + + + + + +