Changeset 1144

Show
Ignore:
Timestamp:
16-11-10 14:57:22 (3 years ago)
Author:
robert@…
Message:

Added an expiry date to the confirmation email for the user and administrator. See ticket #189

Location:
trunk
Files:
8 modified

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/authentication/UserRegistrationController.groovy

    r1138 r1144  
    1717 
    1818class UserRegistrationController { 
     19        static int DAYS_BEFORE_EXPIRY = 3; 
     20        static int DAYS_BEFORE_EXPIRY_ADMIN = 365; 
     21 
    1922    def springSecurityService 
    2023        def authenticationService 
     
    3033     * for confirmation 
    3134     */ 
    32     def add = { 
    33         if( !params.username || !params.email ) { 
    34             flash.message = "You must enter a username and provide an email address" 
    35             render(view: "index", model: [username: params.username, email: params.email]) 
    36             return 
    37         } 
    38  
    39         // Check whether this username already exists 
    40         if( SecUser.findByUsername( params.username ) ) { 
    41             flash.message = "Username already exists" 
    42             render(view: "index", model: [username: params.username, email: params.email]) 
    43             return 
    44         } 
     35    def add = { RegisterCommand command -> 
     36                 
     37                command.validate() 
     38 
     39                if (command.hasErrors()) { 
     40                        def addSendUserLink = false; 
     41 
     42                        // Check the errors and append a link if needed 
     43                        command.errors.allErrors.each { 
     44                                if( it.code == "registerCommand.username.notyetconfirmed" ) { 
     45                                        addSendUserLink = true; 
     46                                } 
     47                        } 
     48 
     49                        flash.message = ""; 
     50            render(view: "index", model: [username: params.username, email: params.email, command: command, addSendUserLink: addSendUserLink]) 
     51                        return 
     52                } 
    4553 
    4654        // Generate a random password 
     
    4856 
    4957        def user = new SecUser( 
    50            username: params.username, 
     58           username     : params.username, 
    5159           email: params.email, 
    5260           password: springSecurityService.encodePassword(password, params.username), 
     
    6270        flash.message = "" 
    6371 
    64         // Create links for the user and administrator to click on. These codes are built from 
    65         // the username and encrypted password. They do not provide 100% security, since the codes 
    66         // could be broken, but it is enough for the confirmation step 
    67         def userCode = ( user.username + user.password + 'user' ).encodeAsMD5(); 
    68         def adminCode = ( user.username + user.password + 'admin' ).encodeAsMD5(); 
    69         def userLink = createLink( controller: 'userRegistration', action: 'confirmUser', params: [id: user.id, code: userCode], absolute: true ) 
    70         def adminLink = createLink( controller: 'userRegistration', action: 'confirmAdmin', params: [id: user.id, code: adminCode], absolute: true ) 
     72                sendUserConfirmationMail( user, password ); 
     73                sendAdminConfirmationMail( user ); 
     74 
     75        // Give the user a nice welcome page 
     76    } 
     77 
     78        def sendUserConfirmation = { 
     79                def user = SecUser.findByUsername( params.username ); 
     80                if( !user ) { 
     81                        flash.message = "No user with this username is found in the database."; 
     82            render(view: "index", model: [username: params.username]) 
     83                } 
     84                if( user.userConfirmed ) { 
     85                        flash.message = "This user has already been confirmed"; 
     86            render(view: "index", model: [username: params.username]) 
     87                } 
     88 
     89                // Remove old registration codes 
     90                RegistrationCode.deleteByUser(user); 
     91 
     92                // Create a new password 
     93        def password = this.generatePassword(8) 
     94                user.password = springSecurityService.encodePassword(password, user.username) 
     95 
     96                // Send a message 
     97                sendUserConfirmationMail(user, password); 
     98 
     99                flash.message = "A new confirmation email has been sent to your registered email address"; 
     100                render(view: "index", model: [username: params.username]) 
     101        } 
     102 
     103        private sendUserConfirmationMail( SecUser user, String password ) { 
     104                def userCode = new RegistrationCode(userId: user.id, expiryDate: new Date() + UserRegistrationController.DAYS_BEFORE_EXPIRY).save( flush: true ); 
     105        def userLink = createLink( controller: 'userRegistration', action: 'confirmUser', params: [code: userCode.token], absolute: true ) 
    71106 
    72107        // Send an email to the user 
    73108        try { 
    74109            sendMail { 
    75                 to      params.email 
     110                to      user.email 
    76111                subject "Registration at GSCF" 
    77                 html    g.render(template:'/email/registrationConfirmationUser', model:[username: user.username, password: password, link: userLink]) 
     112                html    g.render(template:'/email/registrationConfirmationUser', model:[username: user.username, password: password, expiryDate: userCode.expiryDate, link: userLink]) 
    78113            } 
    79114        } catch(Exception e) { 
    80115            log.error "Problem sending email $e.message", e 
    81116            flash.message = 'Email could not be sent' 
    82         } 
     117                        return false 
     118        } 
     119 
     120                return true 
     121        } 
     122 
     123        private sendAdminConfirmationMail( SecUser user ) { 
     124                def adminCode = new RegistrationCode(userId: user.id, expiryDate: new Date() + UserRegistrationController.DAYS_BEFORE_EXPIRY_ADMIN).save(flush: true) 
     125        def adminLink = createLink( controller: 'userRegistration', action: 'confirmAdmin', params: [code: adminCode.token], absolute: true ) 
    83126 
    84127        // Send an email to the administrators 
    85128        try { 
     129                        // Determine administrator email addresses 
    86130            sendMail { 
    87131                to      "gscfproject@gmail.com" 
     
    92136            log.error "Problem sending email $e.message", e 
    93137            flash.message = "Email could not be sent to administrators" 
    94         } 
    95  
    96         // Give the user a nice welcome page 
    97         [username: user.username, password: password] 
    98     } 
     138                        return false 
     139        } 
     140                return true 
     141        } 
     142 
    99143 
    100144    def confirmUser = { 
    101         def code = params.code 
    102         def id = params.id 
    103  
    104         def user = SecUser.findById(id) 
    105  
    106         def generatedCode = ( user.username + user.password + "user" ).encodeAsMD5() 
    107         if( !user || code != generatedCode ) { 
     145        def token = params.code 
     146 
     147                def registrationCode = token ? RegistrationCode.findByToken(token) : null 
     148                if (!registrationCode) { 
    108149            flash.message = "No user found with given parameters. Please make sure you have copied the URL correctly." 
    109150            return 
    110         } 
     151                } 
     152 
     153                if (registrationCode.expiryDate.before(new Date())) { 
     154            flash.message = "Your registration should have been confirmed within " + UserRegistrationController.DAYS_BEFORE_EXPIRY + " days. This confirmation link has expired. Please register again." 
     155            return 
     156                } 
     157 
     158                def user = SecUser.findById(registrationCode.userId) 
    111159 
    112160        if( user.userConfirmed ) { 
     
    117165        user.userConfirmed = true 
    118166        user.save(flush: true) 
     167 
     168                // Remove the registrationCode 
     169                registrationCode.delete(); 
    119170 
    120171        if( user.adminConfirmed ) { 
     
    127178    @Secured(['ROLE_ADMIN', 'IS_AUTHENTICATED_FULLY']) 
    128179    def confirmAdmin = { 
    129         def code = params.code 
    130         def id = params.id 
    131  
    132         def user = SecUser.findById(id) 
    133  
    134         def generatedCode = ( user.username + user.password + "admin" ).encodeAsMD5(); 
    135         if( !user || code != generatedCode ) { 
     180 
     181        def token = params.code 
     182 
     183                def registrationCode = token ? RegistrationCode.findByToken(token) : null 
     184                if (!registrationCode) { 
    136185            flash.message = "No user found with specified code. Please make sure you have copied the URL correctly." 
    137             return; 
    138         } 
     186            return 
     187                } 
     188 
     189                if (registrationCode.expiryDate.before(new Date())) { 
     190            flash.message = "You should have approved this registration within " + UserRegistrationController.DAYS_BEFORE_EXPIRY_ADMIN + " days. This confirmation link has expired." 
     191            return 
     192                } 
     193 
     194                def user = SecUser.findById(registrationCode.userId) 
    139195 
    140196        if( user.adminConfirmed ) { 
     
    145201        user.adminConfirmed = true 
    146202        user.save(flush: true) 
     203 
     204                // Remove the registrationCode 
     205                registrationCode.delete(); 
    147206 
    148207        flash.message = "The registration of " + user.username + " is approved." 
     
    213272                } 
    214273        } 
     274 
     275        static final usernameValidator = { value, command -> 
     276                def user = SecUser.findByUsername( command.username ); 
     277                if( user ) { 
     278            if( user.enabled ) { 
     279                                return "registerCommand.username.unique" 
     280                        } else if( user.dateCreated.after( new Date() - DAYS_BEFORE_EXPIRY ) ) { 
     281                                return "registerCommand.username.notyetconfirmed" 
     282                        } else { 
     283                                RegistrationCode.deleteByUser(user); 
     284                                user.delete(flush:true); 
     285                        } 
     286        } 
     287        } 
     288 
    215289} 
    216290 
     
    231305} 
    232306 
     307class RegisterCommand { 
     308 
     309        String username 
     310        String email 
     311 
     312        static constraints = { 
     313                email(blank: false, email: true) 
     314                username(blank: false, validator: UserRegistrationController.usernameValidator) 
     315        } 
     316} 
  • trunk/grails-app/domain/dbnp/authentication/RegistrationCode.groovy

    r985 r1144  
    1919 */ 
    2020class RegistrationCode { 
    21  
    22         String username 
     21        int userId 
    2322        String token = UUID.randomUUID().toString().replaceAll('-', '') 
    2423        Date dateCreated 
     24        Date expiryDate 
    2525 
    2626        static mapping = { 
    2727                version false 
    2828        } 
     29 
     30        public static boolean deleteByUser( SecUser user ) { 
     31                RegistrationCode.findByUserId(user.id)*.delete(); 
     32                return true; 
     33        } 
    2934} 
  • trunk/grails-app/domain/dbnp/authentication/SecUser.groovy

    r976 r1144  
    66        String password 
    77        String email 
     8        Date dateCreated 
    89        boolean enabled 
    910        boolean accountExpired 
  • trunk/grails-app/i18n/messages.spring-security-ui.properties

    r1138 r1144  
    8080 
    8181registerCommand.username.blank Username is required 
    82 registerCommand.username.unique The username is taken 
     82registerCommand.username.unique The username already exists 
    8383registerCommand.email.blank Email is required 
    8484registerCommand.email.email.invalid Please provide a valid email address 
     
    9797profileCommand.password.maxSize.exceeded Password must be between 8 and 64 characters 
    9898profileCommand.email.email.invalid Please provide a valid email address 
     99 
     100registerCommand.username.unique The username already exists 
     101registerCommand.username.notyetconfirmed This account already exists but has not yet been confirmed. If you are the owner, please check your email. 
     102registerCommand.email.email.invalid Please provide a valid email address 
     103registerCommand.email.blank Please provide a valid email address 
  • trunk/grails-app/views/common/_login_panel.gsp

    r1138 r1144  
    2929                        </div> 
    3030                        <div class="left right"> 
    31                           <g:form url="[action:'add',controller:'userRegistration']" class="clearfix"> 
     31                    <g:form url="[action:'add',controller:'userRegistration']" class="clearfix registration"> 
    3232                                        <input type="hidden" name="targetUri" value="${targetUri}" /> 
    3333                                        <h1>Not a member yet? Sign Up!</h1> 
     34                                          <g:hasErrors bean="${command}"> 
     35                                                  <g:renderErrors bean="${command}" as="list" /> 
     36                                                  <g:if test="${addSendUserLink}"> 
     37                                                                <a class="resend_confirmation" href="<g:createLink url="[action:'sendUserConfirmation',controller:'userRegistration', params: [username: username]]" />">Resend confirmation message</a><br /> 
     38                                                  </g:if> 
     39                                          </g:hasErrors> 
     40 
    3441                                        <label class="grey" for="signup">Username:</label> 
    3542                                        <input class="field" type="text" name="username" id="username" value="${username}" size="23" /> 
     
    4754                        <li class="left">&nbsp;</li> 
    4855                        <li>Hello <sec:ifLoggedIn><sec:username/></sec:ifLoggedIn> 
    49                         <sec:ifNotLoggedIn>Guest</sec:ifNotLoggedIn>!</li> 
     56                                <sec:ifNotLoggedIn>Guest</sec:ifNotLoggedIn>!</li> 
    5057                        <sec:ifLoggedIn> 
    5158                        <li class="sep">|</li> 
  • trunk/grails-app/views/email/_registrationConfirmationUser.gsp

    r976 r1144  
    1616      <p>If you can not click the link, copy this url to the browser:</p> 
    1717      <p>${link}</p> 
     18 
     19      <p>You should confirm your account before <g:formatDate format="dd-MM-yyyy hh:mm" date="${expiryDate}" />, otherwise your account will not be created.</p> 
     20 
    1821      <p>After you have confirmed your registration and the administrator has approved your account, you can login. Your password is:</p> 
    1922      <p><b>${password}</b></p> 
  • trunk/grails-app/views/userRegistration/index.gsp

    r1138 r1144  
    4848    </head> 
    4949    <body> 
     50 
    5051        <div class="body" id="register"> 
    51           <div class="inner"> 
    52             <g:if test="${flash.message}"> <div class='login_message'>${flash.message}</div></g:if> 
     52                <div class='inner' style="margin-top: 160px;"> 
    5353 
    54               <div class='fheader'>Please enter username and email address. </div> 
    55               <form action='/gscf/userRegistration/add' method='POST' id='loginForm' class='cssform' autocomplete='off'> 
    56                       <p> 
    57                               <label for='username'>Username</label> 
    58                               <g:textField name="username" value="${username}" /> 
    59                       </p> 
    60                       <p> 
    61                               <label for='password'>Email address</label> 
    62                               <g:textField name="email" value="${email}" /> 
    63                       </p> 
    64                       <p> 
    65                               <input type='submit' value='Register' /> 
    66                       </p> 
    67               </form> 
    68           </div> 
    69         </div> 
    70   </body> 
     54                        <!-- Registration in is handled in the login panel --> 
     55                        <!-- That's why the registration form is removed here, and the data is moved down --> 
     56 
     57                          <div class='fheader'>Please resolve the errors and try again..</div> 
     58 
     59                </div> 
     60        </div> 
     61        <script type='text/javascript'> 
     62        <!-- 
     63        (function(){ 
     64                // Open login panel 
     65                $("div#panel").slideDown("slow"); 
     66                $("#toggle a").toggle(); 
     67 
     68        })(); 
     69        // --> 
     70        </script> 
     71</body> 
    7172</html> 
  • trunk/web-app/css/login_panel.css

    r959 r1144  
    242242} 
    243243 
     244.registration ul { list-style-type: none; margin-left: 0; padding-left: 0; } 
     245.registration ul li { margin: 3px 0; }