[Xfce4-commits] <moka:master> Add password recover feature.

Nick Schermer noreply at xfce.org
Sun Nov 20 14:30:25 CET 2011


Updating branch refs/heads/master
         to 0025740faa939632bb83e85ac82575d837932e24 (commit)
       from fe72f8aff9b4932809262099c01027e960d2f1d1 (commit)

commit 0025740faa939632bb83e85ac82575d837932e24
Author: Nick Schermer <nick at xfce.org>
Date:   Sat Oct 8 18:59:00 2011 +0200

    Add password recover feature.

 examples/xfce/config.ru                         |    3 +-
 examples/xfce/templates/login_password_canceled |   14 ---
 examples/xfce/templates/login_password_request  |   13 ---
 lib/controllers/authentication.rb               |  113 +++++++++++++++++++----
 lib/controllers/maintainers.rb                  |   12 +--
 lib/helpers/general.rb                          |   23 ++++-
 lib/models/maintainer.rb                        |   15 ++-
 lib/views/email/login_request.erb               |   14 +++
 lib/views/index.haml                            |   17 ++++
 lib/views/layout.haml                           |    6 +-
 lib/views/login.haml                            |   12 ---
 lib/views/login_forgot.haml                     |   49 ++++++++++
 lib/views/login_request.haml                    |    4 +-
 lib/views/maintainer_profile.haml               |    4 +-
 14 files changed, 211 insertions(+), 88 deletions(-)

diff --git a/examples/xfce/config.ru b/examples/xfce/config.ru
index 2d56893..f504686 100755
--- a/examples/xfce/config.ru
+++ b/examples/xfce/config.ru
@@ -78,10 +78,11 @@ end
 
 # global configuration
 Moka::Models::Configuration.load do |conf|
-  conf.set :moka_url, 'https://releases.xfce.org'
+  conf.set :moka_url, 'http://localhost:9292'
   conf.set :archive_dir, '/home/nick/websites/archive.xfce.org/'
   conf.set :mirror, 'http://archive.xfce.org/'
   conf.set :collection_release_pattern, /^([0-9]).([0-9]+)(pre[0-9])?$/
+  conf.set :noreply, 'noreply at xfce.org'
 end
 
 # Uncheck for production environment
diff --git a/examples/xfce/templates/login_password_canceled b/examples/xfce/templates/login_password_canceled
deleted file mode 100644
index 29c7c8f..0000000
--- a/examples/xfce/templates/login_password_canceled
+++ /dev/null
@@ -1,14 +0,0 @@
-A request was canceled from 84.107.218.238.
-
-If you did not request this, it could be either an honest
-mistake or someone attempting to break into your Bugzilla account.
-
-Take a look at the information below and forward this email
-to bugzilla-maintainer at xfce.org if you suspect foul play.
-
-           Token: vgBxX6jqV2
-      Token Type: password
-            User: nick at xfce.org
-      Issue Date: 2011-04-04 23:31:10 CEST
-      Event Data: 84.107.218.238
-Canceled Because: You have logged in.
diff --git a/examples/xfce/templates/login_password_request b/examples/xfce/templates/login_password_request
deleted file mode 100644
index ef23ad0..0000000
--- a/examples/xfce/templates/login_password_request
+++ /dev/null
@@ -1,13 +0,0 @@
-You have (or someone impersonating you has) requested to change your
-Bugzilla password. To complete the change, visit the following link:
-
-https://bugzilla.xfce.org/token.cgi?t=vgBxX6jqV2&a=cfmpw
-
-If you are not the person who made this request, or you wish to cancel
-this request, visit the following link:
-
-https://bugzilla.xfce.org/token.cgi?t=vgBxX6jqV2&a=cxlpw
-
-If you do nothing, the request will lapse after 3 days
-(on April  7, 2011 at 23:31 CEST) or when you
-log in successfully.
diff --git a/lib/controllers/authentication.rb b/lib/controllers/authentication.rb
index cdd5202..417088f 100755
--- a/lib/controllers/authentication.rb
+++ b/lib/controllers/authentication.rb
@@ -3,6 +3,7 @@ require 'pp'
 
 #gem 'warden', '0.2.3'
 require 'warden'
+require 'pony'
 
 module Moka
   module Controllers
@@ -86,25 +87,38 @@ module Moka
         def authentication_user
           env['warden'].user
         end
+
+        def maintainer_forgot(username, token = nil)
+          maintainer = Moka::Models::Maintainer.get(username)
+          expired = 3600 * 2;
+
+          if maintainer and maintainer.active == true
+            if token
+              if (Time.now.to_f - maintainer.token_stamp) > expired or
+                 maintainer.token.empty? or
+                 not maintainer.token.eql? token
+                return nil
+              end
+            else
+              # check if the old token has not expired yet. this to avoid
+              # spam from annoying people
+              if (Time.now.to_f - maintainer.token_stamp) < expired
+                return nil
+              end
+            end
+          end
+          return maintainer
+        end
       end
 
       def self.registered(app)
         app.helpers Helpers
 
         app.get '/login/?' do
-          view :login
+          redirect '/'
         end
 
         app.post '/login/?' do
-
-          maintainer = Moka::Models::Maintainer.get(params['username'])
-
-          if maintainer and maintainer.active == true and maintainer.password == 'invalid'
-            maintainer.password = Digest::SHA1.hexdigest(params['password'])
-            maintainer.save
-            redirect '/'
-          end
-
           env['warden'].authenticate!
           redirect '/'
         end
@@ -119,17 +133,83 @@ module Moka
         end
 
         app.get '/login/forgot' do
+          view :login_forgot
+        end
+
+        app.post '/login/forgot' do
+          maintainer = maintainer_forgot(params[:username])
+          if maintainer
+            chars = ("a".."z").to_a + ("A".."Z").to_a + ("1".."9").to_a
+            maintainer.token = Array.new(10, '').collect{chars[rand(chars.size)]}.join
+            maintainer.token_stamp = Time.now.to_f
+            maintainer.save
+
+            # parameters used in the template
+            params[:token_url] = Moka::Models::Configuration.get(:moka_url) +
+                                 "/login/forgot/" + maintainer.username +
+                                 "/" + maintainer.token
+            params[:token_abort_url] = params[:token_url] + "/cancel"
+            params[:token_expire] = (Time.now + 3600 * 2).to_s
+
+            Pony.mail :to => maintainer.email,
+                      :from => Moka::Models::Configuration.get(:noreply),
+                      :subject => "Xfce Release Manager change password request",
+                      :body => erb(:'email/login_request')
+          end
 
+          env[:step] = "emailed"
           view :login_forgot
         end
 
-        app.get '/login/request' do
+        app.get '/login/forgot/:username/:token' do
+          maintainer = maintainer_forgot(params[:username], params[:token])
+          if maintainer
+            env[:step] = "valid"
+          else
+            env[:step] = "invalid"
+          end
+
+          view :login_forgot
+        end
+
+        app.get '/login/forgot/:username/:token/cancel' do
+          maintainer = Moka::Models::Maintainer.get(params[:username])
+          if maintainer and maintainer.token.eql? params[:token]
+            maintainer.token = nil
+            maintainer.token_stamp = 0
+            maintainer.save
+          end
+
+          env[:step] = "canceled"
+          view :login_forgot
+        end
+
+        app.post '/login/forgot/:username/:token' do
+          maintainer = maintainer_forgot(params[:username], params[:token])
+          if maintainer
+            if validate_password(params[:newpassword], params[:newpassword2])
+              # update password
+              maintainer.password = Digest::SHA1.hexdigest(params['newpassword'])
+              maintainer.token = nil
+              maintainer.token_stamp = 0
+              maintainer.save
 
+              env[:step] = "complete"
+            else
+              env[:step] = "valid"
+            end
+          else
+            env[:step] = "invalid"
+          end
+
+          view :login_forgot
+        end
+
+        app.get '/login/request' do
           view :login_request
         end
 
         app.get '/login/request/sshinfo' do
-
           view :login_request_sshinfo
         end
 
@@ -140,14 +220,11 @@ module Moka
           elsif not Moka::Models::Maintainer.get(params[:username]).nil?
             error_set(:username, 'This username is already taken')
             view :login_request
-          elsif params[:password].empty? or params[:password].length < 6
-            error_set(:password, 'The password must be at least 6 characters long.')
-            view :login_request
-          elsif not params[:password].eql? params[:password2]
-            error_set(:password, 'The two passwords you entered did not match.')
+          elsif not validate_password(params[:password], params[:password2])
+            # error is set in function
             view :login_request
           else
-            @maintainer = Moka::Models::Maintainer.create (:username => params[:username])
+            @maintainer = Moka::Models::Maintainer.create(:username => params[:username])
             @maintainer.email = params[:email]
             @maintainer.realname = params[:realname]
             @maintainer.password = Digest::SHA1.hexdigest(params[:password])
diff --git a/lib/controllers/maintainers.rb b/lib/controllers/maintainers.rb
index 531c5c3..77e6bc6 100755
--- a/lib/controllers/maintainers.rb
+++ b/lib/controllers/maintainers.rb
@@ -27,15 +27,9 @@ module Moka
           # validate the password against the authenticated user
           encrypted_password = Digest::SHA1.hexdigest(params[:password])
           if authentication_user.password.eql? encrypted_password
-            if not params[:new_password].empty?
-              if not params[:new_password].eql? params[:new_password2]
-                error_set(:newpassword, 'The two passwords you entered did not match.')
-              elsif params[:new_password].length < 6
-                error_set(:newpassword, 'The password must be at least 6 characters long.')
-              else
-                encrypted_password = Digest::SHA1.hexdigest(params[:new_password])
-                @maintainer.password = encrypted_password
-              end
+            if not params[:newpassword].empty? and validate_password(params[:newpassword], params[:newpassword2])
+              encrypted_password = Digest::SHA1.hexdigest(params[:newpassword])
+              @maintainer.password = encrypted_password
             end
 
             # put lines in an array and clean it up
diff --git a/lib/helpers/general.rb b/lib/helpers/general.rb
index 02bab1f..b56f2ee 100755
--- a/lib/helpers/general.rb
+++ b/lib/helpers/general.rb
@@ -48,13 +48,24 @@ module Moka
           end
         end
 
-      def maintainer_names(model)
-        names = []
-        for maintainer in model.maintainers.sort
-          names << maintainer.realname
+        def maintainer_names(model)
+          names = []
+          for maintainer in model.maintainers.sort
+            names << maintainer.realname
+          end
+          names.join(', ')
+        end
+        
+        def validate_password(password1, password2)
+          if password1.empty? or password1.length < 6
+              error_set(:newpassword, 'The password must be at least 6 characters long.')
+          elsif password2.empty? or not password1.eql? password2
+              error_set(:newpassword, 'The two passwords you entered did not match.')
+          else
+            return true
+          end
+          return false
         end
-        names.join(', ')
-      end
       end
     end
   end
diff --git a/lib/models/maintainer.rb b/lib/models/maintainer.rb
index d2046b4..efb15aa 100755
--- a/lib/models/maintainer.rb
+++ b/lib/models/maintainer.rb
@@ -1,16 +1,19 @@
 require 'digest/sha1'
+require 'dm-types'
 
 module Moka
   module Models
     class Maintainer
       include DataMapper::Resource
 
-      property :username, String, :key => true
-      property :active,   Boolean, :default  => false
-      property :realname, String
-      property :password, String
-      property :email,    String
-      property :pubkeys,  Text
+      property :username,    String, :key => true
+      property :active,      Boolean, :default  => false
+      property :realname,    String
+      property :password,    String
+      property :email,       String
+      property :token,       String
+      property :token_stamp, Float, :default => 0
+      property :pubkeys,     Text
 
       has n,   :roles,       :through => Resource
       has n,   :collections, :through => Resource
diff --git a/lib/views/email/login_request.erb b/lib/views/email/login_request.erb
new file mode 100644
index 0000000..ab8372a
--- /dev/null
+++ b/lib/views/email/login_request.erb
@@ -0,0 +1,14 @@
+You have (or someone impersonating you has) requested to change your
+Release manager password. To complete the change, visit the following link:
+
+<%= params[:token_url] %>
+
+
+If you are not the person who made this request, or you wish to cancel
+this request, visit the following link:
+
+<%= params[:token_abort_url] %>
+
+
+If you do nothing, the request will lapse after 2 hours
+(<%= params[:token_expire] %>) or when you log in successfully.
diff --git a/lib/views/index.haml b/lib/views/index.haml
index 6057c5b..d7e0654 100644
--- a/lib/views/index.haml
+++ b/lib/views/index.haml
@@ -1 +1,18 @@
 %h2 Welcome
+%p
+  Welcome to the release manager of the Xfce desktop enviromnent. If you
+  want to request developers account, please click the link above.
+
+%h3 Login
+%form{:method => "post", :action => "/login"}
+  - if error(:unauthenticated)
+    %p.error The username or password you entered is not valid. 
+  %p
+    %label{:for => "username"} Username:
+    %input{:type => "text", :name => "username"}
+  %p
+    %label{:for => "password"} Password:
+    %input{:type => "password", :name => "password"}
+  %p
+    %input{:type => "submit", :value => "Log in"}
+    %a{:href => "/login/forgot"} Forgot Password
diff --git a/lib/views/layout.haml b/lib/views/layout.haml
index 8d4bd10..c5784cb 100644
--- a/lib/views/layout.haml
+++ b/lib/views/layout.haml
@@ -20,10 +20,6 @@
           %a{:href => "/logout"} Logout
       - else
         %li
-          %a{:href => "/login"} Log in
-        %li
-          %a{:href => "/login/request"} New Account
-        %li
-          %a{:href => "/login/forgot"} Forgot Password
+          %a{:href => "/login/request"} Request Account
 
     =yield
diff --git a/lib/views/login.haml b/lib/views/login.haml
deleted file mode 100755
index a4ae8bc..0000000
--- a/lib/views/login.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-%h2 Login
-%form{:method => "post", :action => "/login"}
-  - if error(:unauthenticated)
-    %p.error The username or password you entered is not valid. 
-  %p
-    %label{:for => "username"} username:
-    %input{:type => "text", :name => "username"}
-  %p
-    %label{:for => "password"} Password:
-    %input{:type => "password", :name => "password"}
-  %p
-    %input{:type => "submit", :value => "Log in"}
diff --git a/lib/views/login_forgot.haml b/lib/views/login_forgot.haml
index 813f8bc..94bffa0 100755
--- a/lib/views/login_forgot.haml
+++ b/lib/views/login_forgot.haml
@@ -1 +1,50 @@
 %h2 Recover Login
+- if env[:step].eql? "emailed"
+  %p
+    A token for changing your password has been emailed to you. Follow
+    the instructions in that email to change your password.
+  %p
+    %a{:href => "/" } Home
+
+- elsif env[:step].eql? "valid"
+  %p
+    To change your password, enter a new password twice:
+  - if env[:error][:newpassword]
+    %p.error.quote #{env[:error][:newpassword]}
+  %form{:method => "post"}
+    %p
+      %label{:for => "newpassword"} New password:
+      %input{:type => "password", :name => "newpassword"}
+    %p
+      %label{:for => "newpassword2"} Confirm new password:
+      %input{:type => "password", :name => "newpassword2"}
+    %p
+      %input{:type => "submit", :value => "Submit"}
+
+- elsif env[:step].eql? "invalid"
+  %p
+    The token you submitted does not exist, has expired, or has been canceled.
+  %p
+    %a{:href => "/" } Home
+
+- elsif env[:step].eql? "canceled"
+  %p
+    The password request was canceled.
+  %p
+    %a{:href => "/" } Home
+
+- elsif env[:step].eql? "deleted"
+  %p
+    Your password has been changed.
+  %p
+    %a{:href => "/" } Home
+
+- else
+  %p
+    If you have an account, but have forgotten your password, enter your
+    login name below and submit a request to change your password.
+  %form{:method => "post", :action => "/login/forgot"}
+    %p
+      %label{:for => "username"} Username:
+      %input{:type => "text", :name => "username"}
+      %input{:type => "submit", :value => "Reset Password"}
diff --git a/lib/views/login_request.haml b/lib/views/login_request.haml
index d0e6070..7080672 100644
--- a/lib/views/login_request.haml
+++ b/lib/views/login_request.haml
@@ -23,8 +23,8 @@
     %label{:for => "email"} Email address:
     %input{:type => "text", :name => "email", :value => params[:email]}
   %hr
-  - if env[:error][:password]
-    %p.error.quote #{env[:error][:password]}
+  - if env[:error][:newpassword]
+    %p.error.quote #{env[:error][:newpassword]}
   %p
     %label{:for => "password"} Password:
     %input{:type => "password", :name => "password"}
diff --git a/lib/views/maintainer_profile.haml b/lib/views/maintainer_profile.haml
index df2d859..bf16885 100644
--- a/lib/views/maintainer_profile.haml
+++ b/lib/views/maintainer_profile.haml
@@ -23,10 +23,10 @@
     %p.error.quote #{env[:error][:newpassword]}
   %p
     %label{:for => "new_password"} New password:
-    %input{:type => "password", :name => "new_password"}
+    %input{:type => "password", :name => "newpassword"}
   %p
     %label{:for => "new_password2"} Confirm new password:
-    %input{:type => "password", :name => "new_password2"}
+    %input{:type => "password", :name => "newpassword2"}
   %hr
   %p Please enter your existing password to confirm account changes.
   - if env[:error][:password]


More information about the Xfce4-commits mailing list