Changeset 1144


Ignore:
Timestamp:
Nov 16, 2010, 2:57:22 PM (12 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 edited

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; }
Note: See TracChangeset for help on using the changeset viewer.