← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 8591: Web api, implemented controller for creating user accounts with reCaptcha validation support

 

------------------------------------------------------------
revno: 8591
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Fri 2012-10-19 10:00:36 +0200
message:
  Web api, implemented controller for creating user accounts with reCaptcha validation support
added:
  dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java
modified:
  dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml
  dhis-2/dhis-web/dhis-web-commons/src/main/resources/i18n_global.properties


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== added file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java	2012-10-19 08:00:36 +0000
@@ -0,0 +1,244 @@
+package org.hisp.dhis.api.controller;
+
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hisp.dhis.api.utils.ContextUtils;
+import org.hisp.dhis.user.User;
+import org.hisp.dhis.user.UserCredentials;
+import org.hisp.dhis.user.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author Lars Helge Overland
+ */
+@Controller
+@RequestMapping( value = "/account" )
+public class AccountController
+{
+    private static final Log log = LogFactory.getLog( AccountController.class );
+    
+    private static final String RECAPTCHA_VERIFY_URL = "http://www.google.com/recaptcha/api/verify";;
+    private static final String KEY = "6LcM6tcSAAAAAFnHo1f3lLstk3rZv3EVinNROfRq";
+    private static final String TRUE = "true";
+    private static final String FALSE = "false";
+    private static final String SPLIT = "\n";
+    
+    @Autowired
+    private RestTemplate restTemplate;
+    
+    @Autowired
+    private UserService userService;
+    
+    @RequestMapping( method = RequestMethod.POST, produces = ContextUtils.CONTENT_TYPE_TEXT )
+    public @ResponseBody String createAccount( 
+        @RequestParam String username,
+        @RequestParam String firstName,
+        @RequestParam String surname,
+        @RequestParam String password,
+        @RequestParam String email,
+        @RequestParam String phoneNumber,
+        @RequestParam( value = "recaptcha_challenge_field" ) String recapChallenge,
+        @RequestParam( value = "recaptcha_response_field" ) String recapResponse,
+        HttpServletRequest request,
+        HttpServletResponse response )
+    {
+        // ---------------------------------------------------------------------
+        // Trim input
+        // ---------------------------------------------------------------------
+        
+        username = StringUtils.trimToNull( username );
+        firstName = StringUtils.trimToNull( firstName );
+        surname = StringUtils.trimToNull( surname );
+        password = StringUtils.trimToNull( password );
+        email = StringUtils.trimToNull( email );
+        phoneNumber = StringUtils.trimToNull( phoneNumber );
+        recapChallenge = StringUtils.trimToNull( recapChallenge );
+        recapResponse = StringUtils.trimToNull( recapResponse );
+
+        // ---------------------------------------------------------------------
+        // Validate input, return 400 if invalid
+        // ---------------------------------------------------------------------
+        
+        if ( username == null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "User name must be specified";
+        }
+        
+        UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+        
+        if ( credentials != null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "User name is alread taken";
+        }
+        
+        if ( firstName == null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "First name must be specified";
+        }
+
+        if ( surname == null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "Last name must be specified";
+        }
+
+        if ( password == null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "Password must be specified";
+        }
+
+        if ( recapChallenge == null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "Recaptcha challenge must be specified";
+        }
+
+        if ( recapResponse == null )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return "Recaptcha response must be specified";
+        }
+        
+        // ---------------------------------------------------------------------
+        // Check result from API, return 500 if not
+        // ---------------------------------------------------------------------
+        
+        String[] results = checkRecaptcha( KEY, request.getRemoteAddr(), recapChallenge, recapResponse );
+
+        if ( results == null || results.length == 0 )
+        {
+            response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
+            return "Captcha could not be verified due to a server error";
+        }
+
+        // ---------------------------------------------------------------------
+        // Check if verification was successful, return 400 if not
+        // ---------------------------------------------------------------------
+        
+        if ( !TRUE.equalsIgnoreCase( results[0] ) )
+        {
+            response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+            return results.length > 0 ? results[1] : FALSE;
+        }
+
+        // ---------------------------------------------------------------------
+        // Create and save user
+        // ---------------------------------------------------------------------
+        
+        User user = new User();
+        user.setFirstName( firstName );
+        user.setSurname( surname );
+        user.setEmail( email );
+        user.setPhoneNumber( phoneNumber );
+        
+        credentials = new UserCredentials();
+        credentials.setUsername( username );
+        credentials.setPassword( password );
+        credentials.setUser( user );
+        
+        user.setUserCredentials( credentials );
+        
+        // TODO user role and org unit
+        
+        userService.addUser( user );
+        userService.addUserCredentials( credentials );
+        
+        log.info( "Created user successfully with username: " + username );
+        
+        response.setStatus( HttpServletResponse.SC_CREATED );
+        return "Account created";
+    }
+    
+    @RequestMapping( value = "/username/{username}", method = RequestMethod.GET, produces = ContextUtils.CONTENT_TYPE_TEXT )
+    public @ResponseBody String validateUserName( @PathVariable( "username" ) String username )
+    {
+        UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+        
+        return credentials == null ? TRUE : "Username is already taken";
+    }
+    
+    @RequestMapping( value = "/recaptcha", method = RequestMethod.GET, produces = ContextUtils.CONTENT_TYPE_TEXT )
+    public @ResponseBody String validateRecaptcha( 
+        @RequestParam( value = "recaptcha_challenge_field" ) String recapChallenge,
+        @RequestParam( value = "recaptcha_response_field" ) String recapResponse,
+        HttpServletRequest request )
+    {
+        if ( StringUtils.trimToNull( recapChallenge ) == null || StringUtils.trimToNull( recapResponse ) == null )
+        {
+            return FALSE;
+        }
+        
+        String[] results = checkRecaptcha( KEY, request.getRemoteAddr(), recapChallenge, recapResponse );
+        
+        if ( results == null || results.length == 0 )
+        {
+            return FALSE;
+        }
+        
+        return TRUE.equalsIgnoreCase( results[0] ) ? results[0] : ( results.length > 0 ? results[1] : FALSE );
+    }
+
+    // ---------------------------------------------------------------------
+    // Supportive methods
+    // ---------------------------------------------------------------------
+
+    private String[] checkRecaptcha( String privateKey, String remoteIp, String challenge, String response )
+    {
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
+        
+        params.add( "privatekey", privateKey );
+        params.add( "remoteip", remoteIp );
+        params.add( "challenge", challenge );
+        params.add( "response", response );
+
+        String result = restTemplate.postForObject( RECAPTCHA_VERIFY_URL, params, String.class );
+
+        log.info( "Recaptcha result: " + result );
+        
+        return result != null ? result.split( SPLIT ) : null;
+    }    
+}

=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml	2012-10-16 13:26:20 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml	2012-10-19 08:00:36 +0000
@@ -115,6 +115,16 @@
     </action>
 
   </package>
+  
+  <!-- Public -->
+
+  <package name="dhis-web-commons-public" extends="dhis-web-commons" namespace="/dhis-web-commons/public">
+  
+    <action name="account" class="org.hisp.dhis.commons.action.NoAction">
+      <result name="success" type="velocity">/dhis-web-commons/useraccount/account.vm</result>
+    </action>
+  
+  </package>
 
   <!-- Organisation Unit Selection Tree -->
   
@@ -896,6 +906,6 @@
 		/dhis-web-commons/ajax/jsonResponseSuccess.vm</result>
 	  <param name="onExceptionReturn">plainTextError</param>
     </action>
-		
+	
   </package>
 </struts>

=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/i18n_global.properties'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/i18n_global.properties	2012-10-16 14:11:54 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/i18n_global.properties	2012-10-19 08:00:36 +0000
@@ -120,6 +120,9 @@
 gender_male=Male
 gender_female=Female
 gender_other=Other
+password=Password
+confirm_password=Confirm password
+mobile_phone=Mobile phone
 
 #-- PeriodTypes ----------------------------------------------------------------#