Init version pf password reset.

This commit is contained in:
Martin Edenhofer 2012-04-23 08:55:16 +02:00
parent 81a5e0537d
commit a2e5adc4c7
9 changed files with 253 additions and 12 deletions

View file

@ -0,0 +1,67 @@
$ = jQuery.sub()
class Index extends App.Controller
className: 'container'
events:
'submit form': 'submit',
'click .submit': 'submit',
'click .cancel': 'cancel',
constructor: ->
super
# set title
@title 'Reset Password'
@navupdate '#reset_password'
@render()
render: ->
configure_attributes = [
{ name: 'username', display: 'Enter your username or email address:', tag: 'input', type: 'text', limit: 100, null: false, class: 'input span4', },
]
@html App.view('reset_password')(
form: @formGen( model: { configure_attributes: configure_attributes } ),
)
cancel: ->
@navigate 'login'
submit: (e) ->
@log 'submit'
e.preventDefault()
params = @formParam(e.target)
# get data
ajax = new App.Ajax
ajax.ajax(
type: 'POST',
url: '/users/password_reset',
data: JSON.stringify(params),
processData: true,
success: @success
)
success: (data, status, xhr) =>
@html App.view('reset_password_sent')()
error: (xhr, statusText, error) =>
# add notify
Spine.trigger 'notify:removeall'
Spine.trigger 'notify', {
type: 'warning',
msg: 'Wrong Username and Password combination.',
}
# rerender login page
@render(
msg: 'Wrong Username and Password combination.',
username: @username
)
Config.Routes['reset_password'] = Index

View file

@ -14,7 +14,7 @@
<div> <div>
<span class="small"><input name="remember_me" value="1" type="checkbox"/> Remember me</span> <span class="small"><input name="remember_me" value="1" type="checkbox"/> Remember me</span>
<span class="small">&middot;</span> <span class="small">&middot;</span>
<a href="#resend_password" class="small">Forgot password?</a> <a href="#reset_password" class="small">Forgot password?</a>
</div> </div>
</form> </form>
</div> </div>

View file

@ -0,0 +1,13 @@
<div class="hero-unit">
<h2>Forgot your password?<small></small></h2>
<div class="container">
<form>
<p>
<%- @form %>
</p>
<a href="#/" class="btn cancel">Cancel</a>
<button class="btn btn-primary submit">Submit</button>
</form>
</div>
</div>

View file

@ -0,0 +1,9 @@
<div class="hero-unit">
<h2>We've sent password reset instructions to your email address.<small></small></h2>
<div class="container">
<p>
If you don't receive instructions within a minute or two, check your email's spam and junk filters, or try <a href="#reset_password">resending your request</a>.
</p>
</div>
</div>

View file

@ -1,5 +1,5 @@
class UsersController < ApplicationController class UsersController < ApplicationController
before_filter :authentication_check, :except => [:create] before_filter :authentication_check, :except => [:create, :password_reset_send, :password_reset_verify]
# GET /users # GET /users
def index def index
@ -98,4 +98,26 @@ class UsersController < ApplicationController
head :ok head :ok
end end
# POST /users/reset_password
def password_reset_send
puts params.inspect
success = User.password_reset_send( params[:username] )
if success
render :json => { :message => 'ok' }, :status => :ok
else
render :json => { :message => 'failed' }, :status => :unprocessable_entity
end
end
# get /users/verify_password/:hash
def password_reset_verify
success = User.password_reset_verify( params[:hash] )
if success
render :json => { :message => 'ok' }, :status => :ok
else
render :json => { :message => 'failed' }, :status => :unprocessable_entity
end
end
end end

31
app/models/token.rb Normal file
View file

@ -0,0 +1,31 @@
class Token < ActiveRecord::Base
before_create :generate_token
belongs_to :user
def self.check( data )
# fetch token
token = Token.where( :action => data[:action], :name => data[:name] ).first
return if !token
# check if token is still valid
if token.created_at < 1.day.ago
# delete token
token.delete
token.save
return
end
# return token if valid
return token
end
private
def generate_token
begin
self.name = SecureRandom.hex(20)
end while Token.exists?( :name => self.name )
end
end

View file

@ -8,6 +8,7 @@ class User < ApplicationModel
has_and_belongs_to_many :groups, :after_add => :cache_update, :after_remove => :cache_update has_and_belongs_to_many :groups, :after_add => :cache_update, :after_remove => :cache_update
has_and_belongs_to_many :roles, :after_add => :cache_update, :after_remove => :cache_update has_and_belongs_to_many :roles, :after_add => :cache_update, :after_remove => :cache_update
has_and_belongs_to_many :organizations, :after_add => :cache_update, :after_remove => :cache_update has_and_belongs_to_many :organizations, :after_add => :cache_update, :after_remove => :cache_update
has_many :tokens, :after_add => :cache_update, :after_remove => :cache_update
has_many :authorizations, :after_add => :cache_update, :after_remove => :cache_update has_many :authorizations, :after_add => :cache_update, :after_remove => :cache_update
belongs_to :organization, :class_name => 'Organization' belongs_to :organization, :class_name => 'Organization'
@ -47,7 +48,7 @@ class User < ApplicationModel
url = hash['info']['urls']['Website'] || hash['info']['urls']['Twitter'] || '' url = hash['info']['urls']['Website'] || hash['info']['urls']['Twitter'] || ''
end end
roles = Role.where( :name => 'Customer' ) roles = Role.where( :name => 'Customer' )
create( self.create(
:login => hash['info']['nickname'] || hash['uid'], :login => hash['info']['nickname'] || hash['uid'],
:firstname => hash['info']['name'], :firstname => hash['info']['name'],
:email => hash['info']['email'], :email => hash['info']['email'],
@ -61,6 +62,85 @@ class User < ApplicationModel
end end
def self.password_reset_send(username)
puts '2'+username.inspect
return if !username || username == ''
# try to find user based on login
user = User.where( :login => username, :active => true ).first
# try second lookup with email
if !user
user = User.where( :email => username, :active => true ).first
end
# check if email address exists
return if !user.email
# generate token
token = Token.create( :action => 'PasswordReset', :user_id => user.id )
# send mail
data = {}
data[:subject] = 'Reset your #{config.product_name} password'
data[:body] = 'Forgot your password?
We received a request to reset the password for your #{config.product_name} account (#{user.login}).
If you want to reset your password, click on the link below (or copy and paste the URL into your browser):
#{config.http_type}://#{config.fqdn}/password_reset_verify/#{token.name}
This link takes you to a page where you can change your password.
If you don\'t want to reset your password, please ignore this message. Your password will not be reset.
Your #{config.product_name} Team
'
# prepare subject & body
[:subject, :body].each { |key|
data[key.to_sym] = NotificationFactory.build(
:string => data[key.to_sym],
:objects => {
:token => token,
:user => user,
}
)
}
# send notification
NotificationFactory.send(
:recipient => user,
:subject => data[:subject],
:body => data[:body]
)
return true
end
def self.password_reset_check(token)
# check token
token = Token.check( :action => 'PasswordReset', :name => token )
return if !token
return true
end
def self.password_reset_via_token(token,password)
# check token
token = Token.check( :action => 'PasswordReset', :name => token )
return if !token
# reset password
token.user.update_attributes( :password => password )
# delete token
token.delete
token.save
return true
end
def self.find_fulldata(user_id) def self.find_fulldata(user_id)
return cache_get(user_id) if cache_get(user_id) return cache_get(user_id) if cache_get(user_id)

View file

@ -14,6 +14,8 @@ Zammad::Application.routes.draw do
# base objects # base objects
resources :settings, :only => [:create, :show, :index, :update] resources :settings, :only => [:create, :show, :index, :update]
resources :users, :only => [:create, :show, :index, :update] resources :users, :only => [:create, :show, :index, :update]
match '/users/password_reset', :to => 'users#password_reset_send'
match '/users/password_reset_verify', :to => 'users#password_reset_verify'
resources :groups, :only => [:create, :show, :index, :update] resources :groups, :only => [:create, :show, :index, :update]
resources :roles, :only => [:create, :show, :index, :update] resources :roles, :only => [:create, :show, :index, :update]
resources :organizations, :only => [:create, :show, :index, :update] resources :organizations, :only => [:create, :show, :index, :update]

View file

@ -0,0 +1,17 @@
class CreateToken < ActiveRecord::Migration
def up
create_table :tokens do |t|
t.references :user, :null => false
t.string :name, :limit => 100, :null => false
t.string :action, :limit => 40, :null => false
t.timestamps
end
add_index :tokens, :user_id
add_index :tokens, [:name, :action], :unique => true
add_index :tokens, :created_at
end
def down
drop_table :tokens
end
end