Init version pf password reset.
This commit is contained in:
parent
81a5e0537d
commit
a2e5adc4c7
9 changed files with 253 additions and 12 deletions
|
@ -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
|
|
@ -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">·</span>
|
<span class="small">·</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>
|
||||||
|
|
13
app/assets/javascripts/app/views/reset_password.jst.eco
Normal file
13
app/assets/javascripts/app/views/reset_password.jst.eco
Normal 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>
|
|
@ -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>
|
|
@ -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
31
app/models/token.rb
Normal 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
|
|
@ -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)
|
||||||
|
|
|
@ -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]
|
||||||
|
|
17
db/migrate/20120101000080_create_token.rb
Normal file
17
db/migrate/20120101000080_create_token.rb
Normal 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
|
Loading…
Reference in a new issue