Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions app/controllers/organization_subscriptions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class OrganizationSubscriptionsController < ApplicationController
AUTHENTICATION_TOKEN_EXPIRY_TIME = 2.weeks
before_action :validate_token
before_action :skip_authorization

def edit
end

def update
@user.update!(subscribed_organization_ids: params[:user][:subscribed_organization_ids])
flash[:notice] = 'Subscriptions Updated. Thanks!'
redirect_to root_url
end

private

def validate_token
@email_token = params[:token]
@user = User.find_by(email_authentication_token: @email_token)
if @user.nil?
redirect_to root_url
return
end

created_at = @user.email_authentication_created_at

if created_at < AUTHENTICATION_TOKEN_EXPIRY_TIME.ago
flash[:notice] = 'This link has expired.'\
' Please log in to change your email preferences'
redirect_to root_url
return
end
end
end
9 changes: 9 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class User < ActiveRecord::Base

validates_presence_of :first_name, :last_name, :profile
validates_inclusion_of :time_zone, in: ActiveSupport::TimeZone.all.map(&:name), allow_blank: true
validates :email_authentication_token, length: { is: 32 }, allow_nil: true

def self.from_omniauth(omniauth)
authentication = Authentication.where(provider: omniauth['provider'], uid: omniauth['uid'].to_s).first
Expand Down Expand Up @@ -86,4 +87,12 @@ def event_role(event)
def event_checkiner?(event)
event_attendance(event)[:checkiner]
end

def generate_email_authentication_token!
new_token = SecureRandom.base64[0..15] + SecureRandom.base64[0..15]
self.email_authentication_token = new_token
self.email_authentication_created_at = Time.now
return generate_email_authentication_token! unless self.save
new_token
end
end
12 changes: 12 additions & 0 deletions app/views/organization_subscriptions/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%= simple_form_for(@user, :url => "/organization_subscriptions/#{@email_token}", :html => {:method => 'put'}) do |f| %>
<%= devise_error_messages! %>

<div class='form-group'>
<%= f.label :subscribed_organization_ids, 'Allow these Bridge organizations to (potentially) send you email newsletters:' %>
<div class='checkbox-columns'>
<%= f.collection_check_boxes(:subscribed_organization_ids, Organization.order(:name), :id, :name, item_wrapper_class: 'checkbox_row', item_wrapper_tag: 'div') %>
</div>
</div>

<div><%= f.submit 'Update Email Preferences', class: 'btn btn-submit', data: {disable_with: 'Please wait...'} %></div>
<% end %>
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
get :raise_exception
end

get "/organization_subscriptions/:token" => "organization_subscriptions#edit"
put "/organization_subscriptions/:token" => "organization_subscriptions#update"

if Rails.env.development?
get "/style_guide" => "static_pages#style_guide"
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class AddEmailAuthenticationTokenToUser < ActiveRecord::Migration
def change
add_column :users, :email_authentication_token, :string
add_column :users, :email_authentication_created_at, :timestamp

add_index :users, :email_authentication_token, unique: true
end
end
25 changes: 14 additions & 11 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160807055624) do
ActiveRecord::Schema.define(version: 20160820181944) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "unaccent"
Expand Down Expand Up @@ -281,34 +281,37 @@
end

create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0
t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "admin", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "admin", default: false
t.string "first_name"
t.string "last_name"
t.string "time_zone"
t.string "gender"
t.boolean "allow_event_email", default: true
t.boolean "publisher", default: false
t.boolean "spammer", default: false
t.integer "authentications_count", default: 0
t.boolean "allow_event_email", default: true
t.boolean "publisher", default: false
t.boolean "spammer", default: false
t.integer "authentications_count", default: 0
t.string "email_authentication_token"
t.datetime "email_authentication_created_at"
end

add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["email_authentication_token"], name: "index_users_on_email_authentication_token", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

add_foreign_key "authentications", "users"
Expand Down
85 changes: 85 additions & 0 deletions spec/controllers/organization_subscriptions_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
require 'rails_helper'

describe OrganizationSubscriptionsController do
before { @authorization_token = 'andyandhaseebaregarbageteacherss' }


describe '#edit' do
subject { get :edit, token: @authorization_token }

context 'a user exists with the specified email_authorization_token' do
before do
@user = create(:user,
email_authentication_token: @authorization_token,
email_authentication_created_at: Time.now)
end

it 'renders the edit form' do
expect(subject).to render_template(:edit)
end

context 'the token was generated more than 2 weeks ago' do
before { @user.update! email_authentication_created_at: 3.weeks.ago }

it 'redirects to the root' do
expect(subject).to redirect_to(root_url)
expect(flash[:notice]).to match('link has expired.')
end
end
end

context 'a user does not exist with the specified email_authorization_token' do
it 'redirects to the root' do
expect(subject).to redirect_to(root_url)
end
end
end

describe '#update' do
subject do
put :update,
token: @authorization_token,
user: {
subscribed_organization_ids: @subscribed_organizations
}
end

context 'a user exists with the token' do
before do
@user = create(:user,
email_authentication_token: @authorization_token,
email_authentication_created_at: Time.now)

3.times do
create(:organization)
end

@subscribed_organizations = Organization.all.pluck(:id).drop(1)
end

it 'updates the email preferences of the user, without logging in' do
subject
expect(@user.subscribed_organizations)
.to match_array Organization.where(id: @subscribed_organizations)
expect(flash[:notice]).to match(/thanks/i)
expect(controller.current_user).to be_nil
end

context 'the token was generated more than 2 weeks ago' do
before { @user.update! email_authentication_created_at: 3.weeks.ago }

it 'redirects to the root' do
expect {
expect(subject).to redirect_to(root_url)
}.not_to change { @user.reload.subscribed_organizations }
end
end
end

context 'a user does not exist with the token' do
it 'redirects to the root' do
expect(subject).to redirect_to(root_url)
end
end
end
end
20 changes: 20 additions & 0 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,24 @@
expect(@user.profile_path).to eq(Rails.application.routes.url_helpers.user_profile_path(@user))
end
end

describe '#generate_email_authentication_token' do
it 'generates a UID of length 32' do
@user.generate_email_authentication_token!
expect(@user.email_authentication_token.length).to eq 32
end

it 'sets the email_authentication_created_at' do
@user.generate_email_authentication_token!
expect(@user.email_authentication_created_at).to be > 1.minute.ago
end

it 'does not reuse the same token when generated again' do
@user.generate_email_authentication_token!
first_token = @user.email_authentication_token
@user.generate_email_authentication_token!
second_token = @user.email_authentication_token
expect(first_token).to_not eq second_token
end
end
end