From 0fe1678cd37c8e917cb28eed9eb28777d3a92283 Mon Sep 17 00:00:00 2001 From: jessib Date: Tue, 1 Oct 2013 13:56:59 -0700 Subject: Allow admins to view past-due subscriptions. --- app/views/layouts/_navigation.html.haml | 2 +- .../app/controllers/billing_admin_controller.rb | 14 +++++++++++ billing/app/helpers/billing_helper.rb | 27 ++++++++++++++++++++++ billing/app/views/billing_admin/show.html.haml | 17 ++++++++++++++ .../subscriptions/_subscription_details.html.haml | 11 ++++++++- billing/config/routes.rb | 1 + users/app/views/overviews/show.html.haml | 2 +- 7 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 billing/app/controllers/billing_admin_controller.rb create mode 100644 billing/app/views/billing_admin/show.html.haml diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml index b89655f..992aa46 100644 --- a/app/views/layouts/_navigation.html.haml +++ b/app/views/layouts/_navigation.html.haml @@ -3,5 +3,5 @@ = link_to_navigation t(:account_settings), edit_user_path(@user), :active => controller?(:users) - # will want link for identity settings = link_to_navigation t(:support_tickets), auto_tickets_path, :active => controller?(:tickets) - = link_to_navigation t(:billing_settings), show_or_new_customer_link(@user), :active => controller?(:customer, :payments, :subscriptions, :credit_card_info) if APP_CONFIG[:payment].present? + = link_to_navigation t(:billing_settings), billing_top_link(@user), :active => controller?(:customer, :payments, :subscriptions, :credit_card_info) if APP_CONFIG[:payment].present? = link_to_navigation t(:logout), logout_path, :method => :delete diff --git a/billing/app/controllers/billing_admin_controller.rb b/billing/app/controllers/billing_admin_controller.rb new file mode 100644 index 0000000..2a5165c --- /dev/null +++ b/billing/app/controllers/billing_admin_controller.rb @@ -0,0 +1,14 @@ +class BillingAdminController < BillingBaseController + before_filter :authorize_admin + + def show + @past_due_atleast_90_days = Braintree::Subscription.search do |search| + search.days_past_due >= 90 + end + + @all_past_due = Braintree::Subscription.search do |search| + search.status.is Braintree::Subscription::Status::PastDue + end + end + +end diff --git a/billing/app/helpers/billing_helper.rb b/billing/app/helpers/billing_helper.rb index 3c0691f..1dd3f38 100644 --- a/billing/app/helpers/billing_helper.rb +++ b/billing/app/helpers/billing_helper.rb @@ -9,6 +9,15 @@ module BillingHelper form_for object, options, &block end + def billing_top_link(user) + # for admins, top link will show special admin information, which has link to show their own customer information + if (admin? and user == current_user) + billing_admin_path + else + show_or_new_customer_link(user) + end + end + def show_or_new_customer_link(user) # Link to show if user is admin viewing another user, or user is already a customer. # Otherwise link to create a new customer. @@ -19,4 +28,22 @@ module BillingHelper end end + # a bit strange to put here, but we don't have a subscription model + def user_for_subscription(subscription) + + if (transaction = subscription.transactions.first) + # much quicker, but will only work if there is already a transaction associated with subscription (should generally be) + braintree_customer = transaction.customer_details + else + search_results = Braintree::Customer.search do |search| + search.payment_method_token.is subscription.payment_method_token + end + braintree_customer = search_results.first + end + + customer = Customer.find_by_braintree_customer_id(braintree_customer.id) + user = User.find(customer.user_id) + + end + end diff --git a/billing/app/views/billing_admin/show.html.haml b/billing/app/views/billing_admin/show.html.haml new file mode 100644 index 0000000..78843e5 --- /dev/null +++ b/billing/app/views/billing_admin/show.html.haml @@ -0,0 +1,17 @@ +- # todo: move into helper, as following 2 are pretty much identical +%legend= t(:more_than_90_days_past_due) +- if @past_due_atleast_90_days.empty? + = t(:none) +- else + - @past_due_atleast_90_days.each do |past_due_subscription| + = render :partial => "subscriptions/subscription_details", :locals => {:subscription => past_due_subscription, :show_user => user_for_subscription(past_due_subscription)} + +%legend= t(:all_past_due) +- if @all_past_due.empty? + = t(:none) +- else + - @all_past_due.each do |past_due_subscription| + = render :partial => "subscriptions/subscription_details", :locals => {:subscription => past_due_subscription, :show_user => user_for_subscription(past_due_subscription)} + +%legend= t(:your_settings) += link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file diff --git a/billing/app/views/subscriptions/_subscription_details.html.haml b/billing/app/views/subscriptions/_subscription_details.html.haml index 6eda7ca..27b00c7 100644 --- a/billing/app/views/subscriptions/_subscription_details.html.haml +++ b/billing/app/views/subscriptions/_subscription_details.html.haml @@ -1,7 +1,16 @@ %p + - if local_assigns[:show_user] + User: + = link_to show_user.login, user_overview_path(show_user) + ID: = link_to subscription.id, user_subscription_path(@user, subscription.id) Balance: - = number_to_currency(subscription.balance) + - subscription_balance_currency = number_to_currency(subscription.balance) + - if subscription.balance > 0 + %font{:color => "red"} + = subscription_balance_currency + - else + = subscription_balance_currency Bill on: = subscription.billing_day_of_month Start date: diff --git a/billing/config/routes.rb b/billing/config/routes.rb index e024f43..dbdc24b 100644 --- a/billing/config/routes.rb +++ b/billing/config/routes.rb @@ -15,6 +15,7 @@ Rails.application.routes.draw do match 'credit_card_info/confirm' => 'credit_card_info#confirm', :as => :confirm_credit_card_info resources :subscriptions, :only => [:new, :create, :update] # index, show & destroy are within users path + match 'billing_admin' => 'billing_admin#show', :as => :billing_admin #match 'transactions/:product_id/new' => 'transactions#new', :as => :new_transaction #match 'transactions/confirm/:product_id' => 'transactions#confirm', :as => :confirm_transaction diff --git a/users/app/views/overviews/show.html.haml b/users/app/views/overviews/show.html.haml index d3409df..7bea370 100644 --- a/users/app/views/overviews/show.html.haml +++ b/users/app/views/overviews/show.html.haml @@ -19,4 +19,4 @@ %li= icon('user') + link_to(t(:overview_account), edit_user_path(@user)) - # %li= icon('envelope') + link_to(t(:overview_email), {insert path for user identities, presuambly} %li= icon('question-sign') + link_to(t(:overview_tickets), user_tickets_path(@user)) - %li= icon('shopping-cart') + link_to(t(:overview_billing), show_or_new_customer_link(@user)) if APP_CONFIG[:payment].present? + %li= icon('shopping-cart') + link_to(t(:overview_billing), billing_top_link(@user)) if APP_CONFIG[:payment].present? -- cgit v1.2.3 From e4d422142fb2db2153916bed5826651e8418b7a0 Mon Sep 17 00:00:00 2001 From: jessib Date: Thu, 3 Oct 2013 12:06:57 -0700 Subject: Some refactoring of displayed of past-due subscriptions. --- billing/app/helpers/billing_helper.rb | 13 +++++++++++++ billing/app/views/billing_admin/show.html.haml | 14 +++----------- .../views/subscriptions/_subscription_details.html.haml | 9 +++------ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/billing/app/helpers/billing_helper.rb b/billing/app/helpers/billing_helper.rb index 1dd3f38..68ed5b8 100644 --- a/billing/app/helpers/billing_helper.rb +++ b/billing/app/helpers/billing_helper.rb @@ -46,4 +46,17 @@ module BillingHelper end + def show_set_user_subscriptions(set) + if set.empty? + return t(:none) + else + subscriptions_to_display = '' + set.each do |past_due_subscription| + subscriptions_to_display += render :partial => "subscriptions/subscription_details", :locals => {:subscription => past_due_subscription, :show_user => user_for_subscription(past_due_subscription)} + end + subscriptions_to_display.html_safe + end + end + + end diff --git a/billing/app/views/billing_admin/show.html.haml b/billing/app/views/billing_admin/show.html.haml index 78843e5..a275a5e 100644 --- a/billing/app/views/billing_admin/show.html.haml +++ b/billing/app/views/billing_admin/show.html.haml @@ -1,17 +1,9 @@ -- # todo: move into helper, as following 2 are pretty much identical +- # todo: move into helper, as following 2 are pretty much identical (show_set_user_subscriptions is started) %legend= t(:more_than_90_days_past_due) -- if @past_due_atleast_90_days.empty? - = t(:none) -- else - - @past_due_atleast_90_days.each do |past_due_subscription| - = render :partial => "subscriptions/subscription_details", :locals => {:subscription => past_due_subscription, :show_user => user_for_subscription(past_due_subscription)} += show_set_user_subscriptions(@past_due_atleast_90_days) %legend= t(:all_past_due) -- if @all_past_due.empty? - = t(:none) -- else - - @all_past_due.each do |past_due_subscription| - = render :partial => "subscriptions/subscription_details", :locals => {:subscription => past_due_subscription, :show_user => user_for_subscription(past_due_subscription)} += show_set_user_subscriptions(@all_past_due) %legend= t(:your_settings) = link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file diff --git a/billing/app/views/subscriptions/_subscription_details.html.haml b/billing/app/views/subscriptions/_subscription_details.html.haml index 27b00c7..fcf4bc4 100644 --- a/billing/app/views/subscriptions/_subscription_details.html.haml +++ b/billing/app/views/subscriptions/_subscription_details.html.haml @@ -5,12 +5,9 @@ ID: = link_to subscription.id, user_subscription_path(@user, subscription.id) Balance: - - subscription_balance_currency = number_to_currency(subscription.balance) - - if subscription.balance > 0 - %font{:color => "red"} - = subscription_balance_currency - - else - = subscription_balance_currency + - color = (subscription.balance > 0) ? "red" : "" + %font{:color => color} + = number_to_currency(subscription.balance) Bill on: = subscription.billing_day_of_month Start date: -- cgit v1.2.3 From 72af3e2efd8e0c8c399f0765da8bc6d1597fcb6b Mon Sep 17 00:00:00 2001 From: jessib Date: Thu, 3 Oct 2013 12:08:50 -0700 Subject: And removing comment.. --- billing/app/views/billing_admin/show.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/billing/app/views/billing_admin/show.html.haml b/billing/app/views/billing_admin/show.html.haml index a275a5e..3881dc7 100644 --- a/billing/app/views/billing_admin/show.html.haml +++ b/billing/app/views/billing_admin/show.html.haml @@ -1,4 +1,3 @@ -- # todo: move into helper, as following 2 are pretty much identical (show_set_user_subscriptions is started) %legend= t(:more_than_90_days_past_due) = show_set_user_subscriptions(@past_due_atleast_90_days) -- cgit v1.2.3 From fe79b34965145f1ece98644bf1537866880cc230 Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 7 Oct 2013 15:39:56 -0700 Subject: Fix typo in test name. --- billing/test/functional/subsciptions_controller_test.rb | 16 ---------------- billing/test/functional/subscriptions_controller_test.rb | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 billing/test/functional/subsciptions_controller_test.rb create mode 100644 billing/test/functional/subscriptions_controller_test.rb diff --git a/billing/test/functional/subsciptions_controller_test.rb b/billing/test/functional/subsciptions_controller_test.rb deleted file mode 100644 index a6a1057..0000000 --- a/billing/test/functional/subsciptions_controller_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'test_helper' -require 'fake_braintree' - -class SubscriptionsControllerTest < ActionController::TestCase - include CustomerTestHelper - - test "destroy cancels subscription" do - customer = stub_customer - login customer.user - result = Braintree::Subscription.create plan_id: 'my_plan', - payment_method_token: customer.braintree_customer.credit_cards.first.token - subscription = result.subscription - delete :destroy, id: subscription.id, user_id: customer.user.id - assert_equal "Canceled", Braintree::Subscription.find(subscription.id).status - end -end diff --git a/billing/test/functional/subscriptions_controller_test.rb b/billing/test/functional/subscriptions_controller_test.rb new file mode 100644 index 0000000..a6a1057 --- /dev/null +++ b/billing/test/functional/subscriptions_controller_test.rb @@ -0,0 +1,16 @@ +require 'test_helper' +require 'fake_braintree' + +class SubscriptionsControllerTest < ActionController::TestCase + include CustomerTestHelper + + test "destroy cancels subscription" do + customer = stub_customer + login customer.user + result = Braintree::Subscription.create plan_id: 'my_plan', + payment_method_token: customer.braintree_customer.credit_cards.first.token + subscription = result.subscription + delete :destroy, id: subscription.id, user_id: customer.user.id + assert_equal "Canceled", Braintree::Subscription.find(subscription.id).status + end +end -- cgit v1.2.3 From 8b378d916caeaf7fd4b1da2aea45eab4b0ccbb39 Mon Sep 17 00:00:00 2001 From: jessib Date: Tue, 8 Oct 2013 10:44:50 -0700 Subject: Minor tweaks. --- billing/app/views/subscriptions/_subscription_details.html.haml | 2 +- billing/test/functional/customers_controller_test.rb | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/billing/app/views/subscriptions/_subscription_details.html.haml b/billing/app/views/subscriptions/_subscription_details.html.haml index fcf4bc4..3a06c20 100644 --- a/billing/app/views/subscriptions/_subscription_details.html.haml +++ b/billing/app/views/subscriptions/_subscription_details.html.haml @@ -17,7 +17,7 @@ Plan: = subscription.plan_id Price: - = subscription.price + = number_to_currency(subscription.price) - color = (subscription.status == 'Active') ? "green" : "red" Status: %font{:color => color} diff --git a/billing/test/functional/customers_controller_test.rb b/billing/test/functional/customers_controller_test.rb index d4881bf..46c33c9 100644 --- a/billing/test/functional/customers_controller_test.rb +++ b/billing/test/functional/customers_controller_test.rb @@ -7,10 +7,11 @@ class CustomersControllerTest < ActionController::TestCase setup do @user = FactoryGirl.create :user @other_user = FactoryGirl.create :user - FakeBraintree.clear! - FakeBraintree.verify_all_cards! + #FakeBraintree.clear! + #FakeBraintree.verify_all_cards! testid = 'testid' - FakeBraintree::Customer.new({:credit_cards => [{:number=>"5105105105105100", :expiration_date=>"05/2013"}]}, {:id => testid, :merchant_id => Braintree::Configuration.merchant_id}) + #this wasn't actually being used + #FakeBraintree::Customer.new({:credit_cards => [{:number=>"5105105105105100", :expiration_date=>"05/2013"}]}, {:id => testid, :merchant_id => Braintree::Configuration.merchant_id}) # any reason to call the create instance method on the FakeBraintree::Customer ? @customer = Customer.new(:user_id => @other_user.id) @customer.braintree_customer_id = testid @@ -50,6 +51,7 @@ class CustomersControllerTest < ActionController::TestCase test "show" do + skip "show customer" login @other_user # Below will fail, as when we go to fetch the customer data, Braintree::Customer.find(params[:id]) won't find the customer as it is a FakeBraintree customer. #get :show, :id => @customer.braintree_customer_id -- cgit v1.2.3 From 4e471f6b35c012d2825f6be19e24ecd5fef8d636 Mon Sep 17 00:00:00 2001 From: jessib Date: Tue, 8 Oct 2013 14:33:02 -0700 Subject: Consider pending & past due subscriptions as 'active' in the sense that they should prevent one from adding a new subscription. --- billing/app/controllers/subscriptions_controller.rb | 6 +++--- billing/app/models/customer.rb | 6 +++--- billing/app/views/customer/show.html.haml | 2 +- billing/app/views/subscriptions/index.html.haml | 8 ++++---- billing/app/views/subscriptions/show.html.haml | 2 +- billing/config/locales/en.yml | 3 ++- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/billing/app/controllers/subscriptions_controller.rb b/billing/app/controllers/subscriptions_controller.rb index 7689f35..4758adb 100644 --- a/billing/app/controllers/subscriptions_controller.rb +++ b/billing/app/controllers/subscriptions_controller.rb @@ -1,7 +1,7 @@ class SubscriptionsController < BillingBaseController before_filter :authorize before_filter :fetch_subscription, :only => [:show, :destroy] - before_filter :confirm_no_active_subscription, :only => [:new, :create] + before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create] # for now, admins cannot create or destroy subscriptions for others: before_filter :confirm_self, :only => [:new, :create] @@ -38,10 +38,10 @@ class SubscriptionsController < BillingBaseController end - def confirm_no_active_subscription + def confirm_no_pending_active_pastdue_subscription @customer = Customer.find_by_user_id(@user.id) if subscription = @customer.subscriptions # will return active subscription, if it exists - redirect_to subscription_path(subscription.id), :notice => 'You already have an active subscription' + redirect_to user_subscription_path(@user, subscription.id), :notice => 'You already have a subscription' end end diff --git a/billing/app/models/customer.rb b/billing/app/models/customer.rb index f01c300..1acc7a5 100644 --- a/billing/app/models/customer.rb +++ b/billing/app/models/customer.rb @@ -40,19 +40,19 @@ class Customer < CouchRest::Model::Base end # based on 2nd parameter, either returns the single active subscription (or nil if there isn't one), or an array of all subsciptions - def subscriptions(braintree_data=nil, only_active=true) + def subscriptions(braintree_data=nil, only_pending_active_pastdue=true) self.with_braintree_data! return unless has_payment_info? subscriptions = [] self.default_credit_card.subscriptions.each do |sub| - if only_active and sub.status == 'Active' + if only_pending_active_pastdue and ['Pending', 'Active','Past Due'].include? sub.status return sub else subscriptions << sub end end - only_active ? nil : subscriptions + only_pending_active_pastdue ? nil : subscriptions end end diff --git a/billing/app/views/customer/show.html.haml b/billing/app/views/customer/show.html.haml index 243bd3b..562dc4b 100644 --- a/billing/app/views/customer/show.html.haml +++ b/billing/app/views/customer/show.html.haml @@ -18,7 +18,7 @@ = render :partial => "subscriptions/subscription_details", :locals => {:subscription => @active_subscription} - else %p - = t(:no_active_subscription) + = t(:no_relevant_subscription) - if current_user == @user %p .form-actions diff --git a/billing/app/views/subscriptions/index.html.haml b/billing/app/views/subscriptions/index.html.haml index 87771e5..3d4e8fd 100644 --- a/billing/app/views/subscriptions/index.html.haml +++ b/billing/app/views/subscriptions/index.html.haml @@ -1,8 +1,8 @@ %h2=t :all_subscriptions -- active = false +- pending_active_pastdue = false - @subscriptions.each do |s| - - if s.status == 'Active' - - active = true + - if ['Pending', 'Active','Past Due'].include? s.status + - pending_active_pastdue = true = render :partial => "subscription_details", :locals => {:subscription => s} -- if !active and @user == current_user +- if !pending_active_pastdue and @user == current_user = link_to 'subscribe to plan', new_subscription_path, :class => :btn \ No newline at end of file diff --git a/billing/app/views/subscriptions/show.html.haml b/billing/app/views/subscriptions/show.html.haml index 39f4d1a..b258e47 100644 --- a/billing/app/views/subscriptions/show.html.haml +++ b/billing/app/views/subscriptions/show.html.haml @@ -3,4 +3,4 @@ Current Subscription = render :partial => "subscription_details", :locals => {:subscription => @subscription} -= link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if @subscription.status == 'Active' # permission check or should that just be on show? += link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if ['Active', 'Pending'].include? @subscription.status # permission check or should that just be on show? # should you be able to cancel pending subscription? diff --git a/billing/config/locales/en.yml b/billing/config/locales/en.yml index 5245b17..952cfd6 100644 --- a/billing/config/locales/en.yml +++ b/billing/config/locales/en.yml @@ -2,4 +2,5 @@ en: create_new_customer: "Create a new Braintree Customer" must_create_customer: "You must store a customer in braintree before subscribing to a plan" subscribe: "Subscribe" - save_customer_info: "Save Customer Information" \ No newline at end of file + save_customer_info: "Save Customer Information" + no_relevant_subscription: "No subscription which is Active, Pending, or Past Due" \ No newline at end of file -- cgit v1.2.3 From 51f93fc87c9cadbe52877ddc3e7c5fd07866b397 Mon Sep 17 00:00:00 2001 From: jessib Date: Thu, 10 Oct 2013 11:35:26 -0700 Subject: Admins can cancel pastdue subscriptions, but users cannot cancel their own pastdue subscription, as then admins won't be able to search for them. --- billing/app/controllers/billing_admin_controller.rb | 1 + billing/app/controllers/subscriptions_controller.rb | 7 ++++++- billing/app/views/subscriptions/show.html.haml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/billing/app/controllers/billing_admin_controller.rb b/billing/app/controllers/billing_admin_controller.rb index 2a5165c..419a937 100644 --- a/billing/app/controllers/billing_admin_controller.rb +++ b/billing/app/controllers/billing_admin_controller.rb @@ -8,6 +8,7 @@ class BillingAdminController < BillingBaseController @all_past_due = Braintree::Subscription.search do |search| search.status.is Braintree::Subscription::Status::PastDue + #cannot search by balance. end end diff --git a/billing/app/controllers/subscriptions_controller.rb b/billing/app/controllers/subscriptions_controller.rb index 4758adb..3fd5ae5 100644 --- a/billing/app/controllers/subscriptions_controller.rb +++ b/billing/app/controllers/subscriptions_controller.rb @@ -1,6 +1,7 @@ class SubscriptionsController < BillingBaseController before_filter :authorize before_filter :fetch_subscription, :only => [:show, :destroy] + before_filter :only_admin_active_pending, :only => [:destroy] before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create] # for now, admins cannot create or destroy subscriptions for others: before_filter :confirm_self, :only => [:new, :create] @@ -38,9 +39,13 @@ class SubscriptionsController < BillingBaseController end + def only_admin_active_pending + access_denied unless admin? or ['Pending', 'Active'].include? @subscription.status + end + def confirm_no_pending_active_pastdue_subscription @customer = Customer.find_by_user_id(@user.id) - if subscription = @customer.subscriptions # will return active subscription, if it exists + if subscription = @customer.subscriptions # will return pending, active or pastdue subscription, if it exists redirect_to user_subscription_path(@user, subscription.id), :notice => 'You already have a subscription' end end diff --git a/billing/app/views/subscriptions/show.html.haml b/billing/app/views/subscriptions/show.html.haml index b258e47..f4d644a 100644 --- a/billing/app/views/subscriptions/show.html.haml +++ b/billing/app/views/subscriptions/show.html.haml @@ -3,4 +3,4 @@ Current Subscription = render :partial => "subscription_details", :locals => {:subscription => @subscription} -= link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if ['Active', 'Pending'].include? @subscription.status # permission check or should that just be on show? # should you be able to cancel pending subscription? += link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if ['Active', 'Pending'].include? @subscription.status or admin? # permission check or should that just be on show? # should you be able to cancel pending subscription? -- cgit v1.2.3 From 8e5aa5093ab6e67db4f603a44bb7027245b91a21 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 15 Oct 2013 14:31:52 +0200 Subject: detect os in browser and show proper download link We add a class to the html element based on the detected os and use that to pick which download link should be visible. If we detect an os that is not supported we display a deactivated download link instead with all alternatives. --- app/assets/javascripts/application.js | 3 +- app/assets/stylesheets/leap.scss | 85 +++++++++++++++++-- app/helpers/application_helper.rb | 23 ++++-- core/app/assets/javascripts/platform.js | 92 +++++++++++++++++++++ core/app/helpers/core_helper.rb | 38 ++++++++- core/app/views/common/_download_for_os.html.haml | 16 ++++ core/app/views/common/_home_page_buttons.html.haml | 5 +- core/config/locales/en.yml | 12 ++- public/leap-img/128/mask.png | Bin 0 -> 10080 bytes test/integration/home_test.rb | 24 ++++++ 10 files changed, 277 insertions(+), 21 deletions(-) create mode 100644 core/app/assets/javascripts/platform.js create mode 100644 core/app/views/common/_download_for_os.html.haml create mode 100644 public/leap-img/128/mask.png create mode 100644 test/integration/home_test.rb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index cd90934..03a40da 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -16,9 +16,8 @@ //= require bootstrap //= require rails.validations //= require rails.validations.simple_form - //= require leap +//= require platform //= require tickets //= require users - //= require_tree . diff --git a/app/assets/stylesheets/leap.scss b/app/assets/stylesheets/leap.scss index b382773..30f7741 100644 --- a/app/assets/stylesheets/leap.scss +++ b/app/assets/stylesheets/leap.scss @@ -42,26 +42,96 @@ display: none; } +// +// OS specific +// + +.os-android { + display: none !important; +} + +html.android .os-android { + display: inherit !important; +} + +.os-linux32 { + display: none !important; +} + +html.linux .os-linux32 { + display: inherit !important; +} + +.os-linux64 { + display: none !important; +} + +html.linux64 .os-linux64 { + display: inherit !important; +} + +.os-windows { + display: none !important; +} + +html.windows .os-windows { + display: inherit !important; +} + +.os-mac { + display: none !important; +} + +html.mac .os-mac { + display: inherit !important; +} + +.os-other { + display: none !important; +} + +html.oldmac, html.oldwin, html.ios, html.fxos, html.other { + .os-other { + display: inherit !important; + } +} + // // ICONS // -[class^="big-icon-"], -[class*=" big-icon-"] { +[class*="-icon-"] { display: inline-block; - width: 32px; - height: 32px; @include ie7-restore-right-whitespace(); - line-height: 32px; vertical-align: middle; background-repeat: no-repeat; margin-top: 1px; } +[class^="big-icon-"], +[class*=" big-icon-"] { + width: 32px; + height: 32px; + line-height: 32px; +} + +[class^="huge-icon-"], +[class*=" huge-icon-"] { + width: 128px; + height: 128px; + line-height: 128px; +} + + .big-icon-arrow-down { background-image: url(/leap-img/32/arrow-down.png) } +.huge-icon-mask { + height: 64px; + background-image: url(/leap-img/128/mask.png) +} + // // TYPOGRAPHY // @@ -152,6 +222,9 @@ input, textarea { .download { a.btn { width: 14em; + small { + font-weight: normal; + } } } a.btn { @@ -191,4 +264,4 @@ input, textarea { .overview li { padding: 6px 0; -} \ No newline at end of file +} diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1e79990..90e649a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -19,18 +19,25 @@ module ApplicationHelper # http://twitter.github.io/bootstrap/base-css.html#icons # def icon(name, color=nil) + " ".html_safe + end + + def big_icon(name, color=nil) + " ".html_safe + end + + def huge_icon(name, color=nil) + " ".html_safe + end + + def color_class(color) if color.nil? - color_class = nil + nil elsif color == :black - color_class = 'icon-black' + 'icon-black' elsif color == :white - color_class = 'icon-white' + 'icon-white' end - " ".html_safe - end - - def big_icon(name, color=nil) - " ".html_safe end def format_flash(msg) diff --git a/core/app/assets/javascripts/platform.js b/core/app/assets/javascripts/platform.js new file mode 100644 index 0000000..3ab77d7 --- /dev/null +++ b/core/app/assets/javascripts/platform.js @@ -0,0 +1,92 @@ +/* Inspired by mozillas platform detection: + https://github.com/mozilla/bedrock/tree/master/media/js/base +*/ + (function () { + 'use strict'; + function getPlatform() { + var ua = navigator.userAgent, + pf = navigator.platform; + if (/Win(16|9[x58]|NT( [1234]| 5\.0| [^0-9]|[^ -]|$))/.test(ua) || + /Windows ([MC]E|9[x58]|3\.1|4\.10|NT( [1234]| 5\.0| [^0-9]|[^ ]|$))/.test(ua) || + /Windows_95/.test(ua)) { + /** + * Officially unsupported platforms are Windows 95, 98, ME, NT 4.x, 2000 + * These regular expressions match: + * - Win16 + * - Win9x + * - Win95 + * - Win98 + * - WinNT (not followed by version or followed by version <= 5) + * - Windows ME + * - Windows CE + * - Windows 9x + * - Windows 95 + * - Windows 98 + * - Windows 3.1 + * - Windows 4.10 + * - Windows NT (not followed by version or followed by version <= 5) + * - Windows_95 + */ + return 'oldwin'; + } + if (ua.indexOf("MSIE 6.0") !== -1 && + ua.indexOf("Windows NT 5.1") !== -1 && + ua.indexOf("SV1") === -1) { + // Windows XP SP1 + return 'oldwin'; + } + if (pf.indexOf("Win32") !== -1 || + pf.indexOf("Win64") !== -1) { + return 'windows'; + } + if (/android/i.test(ua)) { + return 'android'; + } + if (/armv[6-7]l/.test(pf)) { + return 'android'; + } + if (pf.indexOf("Linux") !== -1) { + if (pf.indexOf("64") !== -1) { + return 'linux64'; + } else { + return 'linux32'; + } + } + if (pf.indexOf("MacPPC") !== -1) { + return 'oldmac'; + } + if (/Mac OS X 10.[0-5]/.test(ua)) { + return 'oldmac'; + } + if (pf.indexOf('iPhone') !== -1 || + pf.indexOf('iPad') !== -1 || + pf.indexOf('iPod') !== -1 ) { + return 'ios'; + } + if (ua.indexOf("Mac OS X") !== -1) { + return 'osx'; + } + if (ua.indexOf("MSIE 5.2") !== -1) { + return 'oldmac'; + } + if (pf.indexOf("Mac") !== -1) { + return 'oldmac'; + } + if (navigator.platform === '' && + navigator.userAgent.indexOf("Firefox") !== -1 && + navigator.userAgent.indexOf("Mobile") !== -1) { + return 'fxos'; + } + + return 'other'; + } + (function () { + // Immediately set the platform classname on the html-element + // to avoid lots of flickering + var h = document.documentElement; + window.site = { + platform : getPlatform() + }; + h.className = window.site.platform; + })(); + })(); diff --git a/core/app/helpers/core_helper.rb b/core/app/helpers/core_helper.rb index a496144..29a4700 100644 --- a/core/app/helpers/core_helper.rb +++ b/core/app/helpers/core_helper.rb @@ -10,4 +10,40 @@ module CoreHelper render 'common/home_page_buttons' end -end \ No newline at end of file + def available_clients + CLIENT_AVAILABILITY + end + + def alternative_client_links(os = nil) + alternative_clients(os).map do |client| + link_to(client.capitalize, client_download_url(client)) + end + end + + def alternative_clients(os = nil) + CLIENT_AVAILABILITY - [os] + end + + def client_download_url(os = nil) + client_download_domain(os) + client_download_path(os) + end + + def client_download_domain(os) + "https://downloads.leap.se" + end + + def client_download_path(os) + CLIENT_DOWNLOAD_PATHS[os] || '/client' + end + + + CLIENT_AVAILABILITY = %w/linux32 linux64 mac windows android/ + CLIENT_DOWNLOAD_PATHS = { + android: '/client/android', + linux: '/client/linux', + linux32: '/client/linux/Bitmask-linux32-latest.tar.bz2', + linux64: '/client/linux/Bitmask-linux64-latest.tar.bz2', + osx: '/client/osx/Bitmask-OSC-latest.dmg', + windows: '/client/windows' + }.with_indifferent_access +end diff --git a/core/app/views/common/_download_for_os.html.haml b/core/app/views/common/_download_for_os.html.haml new file mode 100644 index 0000000..b7c88ba --- /dev/null +++ b/core/app/views/common/_download_for_os.html.haml @@ -0,0 +1,16 @@ +- os = download_for_os +%div{:class => "os-#{os}"} + %span.link + - btn_class = (os == "other") ? "disabled" : "btn-primary" + = link_to client_download_url(os), :class => "btn btn-large #{btn_class}" do + .pull-left= huge_icon('mask') + = t(:download_client) + %br/ + %small= I18n.t("os.#{os}") + %span.info + = t(:client_info, :provider => content_tag(:b,APP_CONFIG[:domain])).html_safe + %br/ + - if os == "other" + = t(:all_downloads_info, :clients => alternative_client_links(os).to_sentence).html_safe + - else + = t(:other_downloads_info, :clients => alternative_client_links(os).to_sentence).html_safe diff --git a/core/app/views/common/_home_page_buttons.html.haml b/core/app/views/common/_home_page_buttons.html.haml index 7eb4c40..b87c867 100644 --- a/core/app/views/common/_home_page_buttons.html.haml +++ b/core/app/views/common/_home_page_buttons.html.haml @@ -1,11 +1,10 @@ - icon_color = :black -.home-buttons +.home-buttons.linux64 .row-fluid.first .span3 .download.span6 - %span.link= link_to(big_icon('arrow-down', icon_color) + t(:download_client), "https://downloads.leap.se/client", :class => 'btn btn-large') - %span.info= t(:download_client_info, :provider => content_tag(:b,APP_CONFIG[:domain])).html_safe + = render partial: 'common/download_for_os', collection: available_clients + ['other'] .span3 .row-fluid.second .login.span4 diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 25b377a..344eee4 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -21,10 +21,20 @@ en: are_you_sure: "Are you sure? This change cannot be undone." download_client: "Download Bitmask" - download_client_info: "The Bitmask application allows you to use %{provider} services. It is available for Linux, Mac, Windows, and Android." + client_info: "The Bitmask application allows you to use %{provider} services." + all_downloads_info: "It is available for %{clients}." + other_downloads_info: "It is also available for %{clients}." login_info: "Log in to change your account settings, create support tickets, and manage payments." signup_info: "Sign up for a new user account via this website (it is better if you use the Bitmask application to sign up, but this website works too)." welcome: "Welcome to %{provider}." get_help: "Get Help" help_info: "Can't login? Create a new support ticket anonymously." example_email: 'user@domain.org' + os: + linux32: "Linux (32 bit)" + linux64: "Linux (64 bit)" + windows: "Windows" + android: "Android" + mac: "Mac OSX" + other: "(not available for your OS.)" + diff --git a/public/leap-img/128/mask.png b/public/leap-img/128/mask.png new file mode 100644 index 0000000..c7390eb Binary files /dev/null and b/public/leap-img/128/mask.png differ diff --git a/test/integration/home_test.rb b/test/integration/home_test.rb new file mode 100644 index 0000000..126a420 --- /dev/null +++ b/test/integration/home_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class AccountTest < BrowserIntegrationTest + + setup do + Capybara.current_driver = Capybara.javascript_driver + end + + test "old windows shows deactivated download" do + page.driver.headers = { "User-Agent" => "Win98" } + visit '/' + assert_selector "html.oldwin" + assert has_text? "not available" + end + + test "android shows android download" do + page.driver.headers = { "User-Agent" => "Android" } + visit '/' + assert_selector "html.android" + assert has_no_text? "not available" + assert_selector "small", text: "Android" + end + +end -- cgit v1.2.3 From a6f32017f5c7802798f10e2f4041037fb5684def Mon Sep 17 00:00:00 2001 From: jessib Date: Tue, 15 Oct 2013 15:08:35 -0700 Subject: Add permissions to subscriptions index, and fix test to stub the subscription's balance. --- billing/app/controllers/subscriptions_controller.rb | 6 ++++++ billing/test/integration/subscription_test.rb | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/billing/app/controllers/subscriptions_controller.rb b/billing/app/controllers/subscriptions_controller.rb index 3fd5ae5..0a1c733 100644 --- a/billing/app/controllers/subscriptions_controller.rb +++ b/billing/app/controllers/subscriptions_controller.rb @@ -2,6 +2,7 @@ class SubscriptionsController < BillingBaseController before_filter :authorize before_filter :fetch_subscription, :only => [:show, :destroy] before_filter :only_admin_active_pending, :only => [:destroy] + before_filter :confirm_self_or_admin, :only => [:index] before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create] # for now, admins cannot create or destroy subscriptions for others: before_filter :confirm_self, :only => [:new, :create] @@ -17,6 +18,7 @@ class SubscriptionsController < BillingBaseController def create @result = Braintree::Subscription.create( :payment_method_token => params[:payment_method_token], :plan_id => params[:plan_id] ) + #if you want to test pastdue, can add :price => '2001', :trial_period => true,:trial_duration => 1,:trial_duration_unit => "day" and then wait a day end def destroy @@ -54,4 +56,8 @@ class SubscriptionsController < BillingBaseController @user == current_user end + def confirm_self_or_admin + access_denied unless confirm_self or admin? + end + end diff --git a/billing/test/integration/subscription_test.rb b/billing/test/integration/subscription_test.rb index b893896..6356177 100644 --- a/billing/test/integration/subscription_test.rb +++ b/billing/test/integration/subscription_test.rb @@ -10,28 +10,34 @@ class SubscriptionTest < ActionDispatch::IntegrationTest setup do Warden.test_mode! - @admin = stub_record :user, :admin => true + @admin = User.find_by_login('admin') || FactoryGirl.create(:user, login: 'admin') @customer = stub_customer @braintree_customer = @customer.braintree_customer response = Braintree::Subscription.create plan_id: '5', - payment_method_token: @braintree_customer.credit_cards.first.token + payment_method_token: @braintree_customer.credit_cards.first.token, + price: '10' @subscription = response.subscription Capybara.current_driver = Capybara.javascript_driver end teardown do Warden.test_reset! + @admin.destroy end - test "admin can see subscription for another" do + test "admin can see all subscriptions for another" do login_as @admin @customer.stubs(:subscriptions).returns([@subscription]) + @subscription.stubs(:balance).returns 0 visit user_subscriptions_path(@customer.user_id) assert page.has_content?("Subscriptions") assert page.has_content?("Status: Active") page.save_screenshot('/tmp/subscriptions.png') end + # test "user cannot see all subscriptions for other user" do + #end + #test "admin cannot add subscription for another" do #end -- cgit v1.2.3 From 4618bf296e735f47561dc1ddcccaaa701cae9daa Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 16 Oct 2013 11:25:58 +0200 Subject: remove leftover from testing os specific sections --- core/app/views/common/_home_page_buttons.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/app/views/common/_home_page_buttons.html.haml b/core/app/views/common/_home_page_buttons.html.haml index b87c867..e10fd38 100644 --- a/core/app/views/common/_home_page_buttons.html.haml +++ b/core/app/views/common/_home_page_buttons.html.haml @@ -1,6 +1,6 @@ - icon_color = :black -.home-buttons.linux64 +.home-buttons .row-fluid.first .span3 .download.span6 -- cgit v1.2.3 From 155c2d25395a02916543131348a703cd13573d4c Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 17 Oct 2013 10:13:44 +0200 Subject: use latest version of srp_js to fix #4002 We were not encoding the srp password properly before. So umlauts in the password would cause the login procedure to fail. --- users/app/assets/javascripts/srp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/app/assets/javascripts/srp b/users/app/assets/javascripts/srp index d22bf3b..8f33d32 160000 --- a/users/app/assets/javascripts/srp +++ b/users/app/assets/javascripts/srp @@ -1 +1 @@ -Subproject commit d22bf3b9fe2fd31192e1e1b358e97e5a0f3f90b3 +Subproject commit 8f33d32d40b1e21ae7fb9a92c78a275422af4217 -- cgit v1.2.3 From 9f4b1bcf315f09fd6d302ad187281ec4ed443f04 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 17 Oct 2013 12:05:26 +0200 Subject: blacklist system logins for aliases and logins We blacklist based on three things: * blacklist in APP_CONFIG[:handle_blacklist] * emails in RFC 2142 * usernames in /etc/passwd The latter two can be allowed by explicitly whitelisting them in APP_CONFIG[:handle_whitelist]. We stick to blocking names that have been configured as both blacklisted and whitelisted - better be save than sorry. --- config/defaults.yml | 7 ++++++- users/app/models/local_email.rb | 33 +++++++++++++++++++++++++++++++++ users/test/unit/local_email_test.rb | 31 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/config/defaults.yml b/config/defaults.yml index 8d81668..66ec639 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -18,6 +18,11 @@ common: &common pagination_size: 30 auth: token_expires_after: 60 + # handles that will be blocked from being used as logins or email aliases + # in addition to the ones in /etc/passwd and http://tools.ietf.org/html/rfc2142 + handle_blacklist: [certmaster, ssladmin, arin-admin, administrator, www-data, maildrop] + # handles that will be allowed despite being in /etc/passwd or rfc2142 + handle_whitelist: [] development: <<: *dev_ca @@ -43,4 +48,4 @@ production: admins: [] domain: example.net payment: [] -# logfile: /path/to/your/logs + # logfile: /path/to/your/logs diff --git a/users/app/models/local_email.rb b/users/app/models/local_email.rb index 6303bb6..2b4c65e 100644 --- a/users/app/models/local_email.rb +++ b/users/app/models/local_email.rb @@ -1,5 +1,10 @@ class LocalEmail < Email + BLACKLIST_FROM_RFC2142 = [ + 'postmaster', 'hostmaster', 'domainadmin', 'webmaster', 'www', + 'abuse', 'noc', 'security', 'usenet', 'news', 'uucp', + 'ftp', 'sales', 'marketing', 'support', 'info' + ] def self.domain APP_CONFIG[:domain] @@ -11,6 +16,8 @@ class LocalEmail < Email :message => "needs to end in @#{domain}" } + validate :handle_allowed + def initialize(s) super append_domain_if_needed @@ -32,4 +39,30 @@ class LocalEmail < Email end end + def handle_allowed + errors.add(:handle, "is reserved.") if handle_reserved? + end + + def handle_reserved? + # *ARRAY in a case statement tests if ARRAY includes the handle. + case handle + when *APP_CONFIG[:handle_blacklist] + true + when *APP_CONFIG[:handle_whitelist] + false + when *BLACKLIST_FROM_RFC2142 + true + else + handle_in_passwd? + end + end + + def handle_in_passwd? + begin + !!Etc.getpwnam(handle) + rescue ArgumentError + # handle was not found + return false + end + end end diff --git a/users/test/unit/local_email_test.rb b/users/test/unit/local_email_test.rb index b25f46f..20ee7f1 100644 --- a/users/test/unit/local_email_test.rb +++ b/users/test/unit/local_email_test.rb @@ -24,6 +24,37 @@ class LocalEmailTest < ActiveSupport::TestCase assert_equal ["needs to end in @#{LocalEmail.domain}"], local.errors[:email] end + test "blacklists rfc2142" do + black_listed = LocalEmail.new('hostmaster') + assert !black_listed.valid? + end + + test "blacklists etc passwd" do + black_listed = LocalEmail.new('nobody') + assert !black_listed.valid? + end + + test "whitelist overwrites automatic blacklists" do + with_config handle_whitelist: ['nobody', 'hostmaster'] do + white_listed = LocalEmail.new('nobody') + assert white_listed.valid? + white_listed = LocalEmail.new('hostmaster') + assert white_listed.valid? + end + end + + test "blacklists from config" do + black_listed = LocalEmail.new('www-data') + assert !black_listed.valid? + end + + test "blacklist from config overwrites whitelist" do + with_config handle_whitelist: ['www-data'] do + black_listed = LocalEmail.new('www-data') + assert !black_listed.valid? + end + end + def handle @handle ||= Faker::Internet.user_name end -- cgit v1.2.3 From 92cb054d53aaac6864a6a805d9cdd3919f4a38bc Mon Sep 17 00:00:00 2001 From: jessib Date: Thu, 17 Oct 2013 13:58:54 -0700 Subject: Some cleanup of code to deal with past due subscriptions. --- .../app/controllers/billing_admin_controller.rb | 18 +++++++++++++++-- .../app/controllers/subscriptions_controller.rb | 6 +++--- billing/app/helpers/billing_helper.rb | 23 ++++++---------------- billing/app/views/billing_admin/show.html.haml | 5 ++--- .../subscriptions/_subscription_details.html.haml | 3 ++- billing/app/views/subscriptions/show.html.haml | 2 +- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/billing/app/controllers/billing_admin_controller.rb b/billing/app/controllers/billing_admin_controller.rb index 419a937..cd6149f 100644 --- a/billing/app/controllers/billing_admin_controller.rb +++ b/billing/app/controllers/billing_admin_controller.rb @@ -2,14 +2,28 @@ class BillingAdminController < BillingBaseController before_filter :authorize_admin def show - @past_due_atleast_90_days = Braintree::Subscription.search do |search| + + br_atleast_90_days = Braintree::Subscription.search do |search| search.days_past_due >= 90 end + @past_due_atleast_90_days = braintree_resource_collection_to_array(br_atleast_90_days) - @all_past_due = Braintree::Subscription.search do |search| + br_all_past_due = Braintree::Subscription.search do |search| search.status.is Braintree::Subscription::Status::PastDue #cannot search by balance. end + @all_past_due = braintree_resource_collection_to_array(br_all_past_due) + + end + + private + + def braintree_resource_collection_to_array(braintree_resource_collection) + array = [] + braintree_resource_collection.each do |object| + array << object + end + array end end diff --git a/billing/app/controllers/subscriptions_controller.rb b/billing/app/controllers/subscriptions_controller.rb index 0a1c733..01aaab4 100644 --- a/billing/app/controllers/subscriptions_controller.rb +++ b/billing/app/controllers/subscriptions_controller.rb @@ -1,7 +1,7 @@ class SubscriptionsController < BillingBaseController before_filter :authorize before_filter :fetch_subscription, :only => [:show, :destroy] - before_filter :only_admin_active_pending, :only => [:destroy] + before_filter :confirm_cancel_subscription, :only => [:destroy] before_filter :confirm_self_or_admin, :only => [:index] before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create] # for now, admins cannot create or destroy subscriptions for others: @@ -41,8 +41,8 @@ class SubscriptionsController < BillingBaseController end - def only_admin_active_pending - access_denied unless admin? or ['Pending', 'Active'].include? @subscription.status + def confirm_cancel_subscription + access_denied unless view_context.allow_cancel_subscription(@subscription) end def confirm_no_pending_active_pastdue_subscription diff --git a/billing/app/helpers/billing_helper.rb b/billing/app/helpers/billing_helper.rb index 68ed5b8..b9e5e2e 100644 --- a/billing/app/helpers/billing_helper.rb +++ b/billing/app/helpers/billing_helper.rb @@ -33,30 +33,19 @@ module BillingHelper if (transaction = subscription.transactions.first) # much quicker, but will only work if there is already a transaction associated with subscription (should generally be) - braintree_customer = transaction.customer_details + braintree_customer_id = transaction.customer_details.id else - search_results = Braintree::Customer.search do |search| - search.payment_method_token.is subscription.payment_method_token - end - braintree_customer = search_results.first + credit_card = Braintree::CreditCard.find(subscription.payment_method_token) + braintree_customer_id = credit_card.customer_id end - customer = Customer.find_by_braintree_customer_id(braintree_customer.id) + customer = Customer.find_by_braintree_customer_id(braintree_customer_id) user = User.find(customer.user_id) end - def show_set_user_subscriptions(set) - if set.empty? - return t(:none) - else - subscriptions_to_display = '' - set.each do |past_due_subscription| - subscriptions_to_display += render :partial => "subscriptions/subscription_details", :locals => {:subscription => past_due_subscription, :show_user => user_for_subscription(past_due_subscription)} - end - subscriptions_to_display.html_safe - end + def allow_cancel_subscription(subscription) + ['Active', 'Pending'].include? subscription.status or (admin? and subscription.status == 'Past Due') end - end diff --git a/billing/app/views/billing_admin/show.html.haml b/billing/app/views/billing_admin/show.html.haml index 3881dc7..0382cf0 100644 --- a/billing/app/views/billing_admin/show.html.haml +++ b/billing/app/views/billing_admin/show.html.haml @@ -1,8 +1,7 @@ %legend= t(:more_than_90_days_past_due) -= show_set_user_subscriptions(@past_due_atleast_90_days) - += render(:partial => "subscriptions/subscription_details", :collection => @past_due_atleast_90_days, :as => 'subscription', :locals => {:show_user => true}) || t(:none) %legend= t(:all_past_due) -= show_set_user_subscriptions(@all_past_due) += render(:partial => "subscriptions/subscription_details", :collection => @all_past_due, :as => 'subscription', :locals => {:show_user => true}) || t(:none) %legend= t(:your_settings) = link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file diff --git a/billing/app/views/subscriptions/_subscription_details.html.haml b/billing/app/views/subscriptions/_subscription_details.html.haml index 3a06c20..6145c95 100644 --- a/billing/app/views/subscriptions/_subscription_details.html.haml +++ b/billing/app/views/subscriptions/_subscription_details.html.haml @@ -1,7 +1,8 @@ %p - if local_assigns[:show_user] User: - = link_to show_user.login, user_overview_path(show_user) + - user_to_show = user_for_subscription(subscription) + = link_to user_to_show.login, user_overview_path(user_to_show) ID: = link_to subscription.id, user_subscription_path(@user, subscription.id) Balance: diff --git a/billing/app/views/subscriptions/show.html.haml b/billing/app/views/subscriptions/show.html.haml index f4d644a..2699db9 100644 --- a/billing/app/views/subscriptions/show.html.haml +++ b/billing/app/views/subscriptions/show.html.haml @@ -3,4 +3,4 @@ Current Subscription = render :partial => "subscription_details", :locals => {:subscription => @subscription} -= link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if ['Active', 'Pending'].include? @subscription.status or admin? # permission check or should that just be on show? # should you be able to cancel pending subscription? += link_to t(:cancel_subscription), user_subscription_path(@user, @subscription.id), :confirm => t(:are_you_sure), :method => :delete, :class => 'btn btn-danger' if allow_cancel_subscription(@subscription) -- cgit v1.2.3 From 1384f6c43dde6a19f270416e34e39130a3d0a53d Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 18 Oct 2013 09:50:37 +0200 Subject: Make download links configurable This way we won't have to redeploy once the new links to the windows and the android version are there. Also this obviously offers more flexibility for providers. --- config/defaults.yml | 21 +++++++++++++++++++++ core/app/helpers/core_helper.rb | 36 ------------------------------------ core/app/helpers/download_helper.rb | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 core/app/helpers/download_helper.rb diff --git a/config/defaults.yml b/config/defaults.yml index 66ec639..6211e37 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -13,6 +13,23 @@ cert_options: &cert_options limited_cert_prefix: "LIMITED" unlimited_cert_prefix: "UNLIMITED" +downloads: &downloads + client_download_domain: https://downloads.leap.se + available_clients: + - linux32 + - linux64 + - mac + - windows + - android + download_paths: + android: /client/android + linux: /client/linux + linux32: /client/linux/Bitmask-linux32-latest.tar.bz2 + linux64: /client/linux/Bitmask-linux64-latest.tar.bz2 + osx: /client/osx/Bitmask-OSC-latest.dmg + windows: /client/windows + other: /client + common: &common force_ssl: false pagination_size: 30 @@ -24,7 +41,9 @@ common: &common # handles that will be allowed despite being in /etc/passwd or rfc2142 handle_whitelist: [] + development: + <<: *downloads <<: *dev_ca <<: *cert_options <<: *common @@ -34,6 +53,7 @@ development: payment: [] test: + <<: *downloads <<: *dev_ca <<: *cert_options <<: *common @@ -43,6 +63,7 @@ test: payment: [billing] production: + <<: *downloads <<: *cert_options <<: *common admins: [] diff --git a/core/app/helpers/core_helper.rb b/core/app/helpers/core_helper.rb index 29a4700..4126906 100644 --- a/core/app/helpers/core_helper.rb +++ b/core/app/helpers/core_helper.rb @@ -10,40 +10,4 @@ module CoreHelper render 'common/home_page_buttons' end - def available_clients - CLIENT_AVAILABILITY - end - - def alternative_client_links(os = nil) - alternative_clients(os).map do |client| - link_to(client.capitalize, client_download_url(client)) - end - end - - def alternative_clients(os = nil) - CLIENT_AVAILABILITY - [os] - end - - def client_download_url(os = nil) - client_download_domain(os) + client_download_path(os) - end - - def client_download_domain(os) - "https://downloads.leap.se" - end - - def client_download_path(os) - CLIENT_DOWNLOAD_PATHS[os] || '/client' - end - - - CLIENT_AVAILABILITY = %w/linux32 linux64 mac windows android/ - CLIENT_DOWNLOAD_PATHS = { - android: '/client/android', - linux: '/client/linux', - linux32: '/client/linux/Bitmask-linux32-latest.tar.bz2', - linux64: '/client/linux/Bitmask-linux64-latest.tar.bz2', - osx: '/client/osx/Bitmask-OSC-latest.dmg', - windows: '/client/windows' - }.with_indifferent_access end diff --git a/core/app/helpers/download_helper.rb b/core/app/helpers/download_helper.rb new file mode 100644 index 0000000..f9c6c40 --- /dev/null +++ b/core/app/helpers/download_helper.rb @@ -0,0 +1,33 @@ +module DownloadHelper + + def alternative_client_links(os = nil) + alternative_clients(os).map do |client| + link_to(client.capitalize, client_download_url(client)) + end + end + + def alternative_clients(os = nil) + available_clients - [os] + end + + def client_download_url(os = nil) + client_download_domain + client_download_path(os) + end + + def client_download_path(os) + download_paths[os.to_s] || download_paths['other'] || '' + end + + def available_clients + APP_CONFIG[:available_clients] || [] + end + + def client_download_domain + APP_CONFIG[:client_download_domain] || '' + end + + def download_paths + APP_CONFIG[:download_paths] || {} + end + +end -- cgit v1.2.3 From ed3b83e31a16732f309eb470358bf06c1190b4f8 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 18 Oct 2013 10:24:02 +0200 Subject: Version 0.2.5 * detect os in browser and show proper download link (#4173) * billing: admin can see past due subscriptions * passwords with umlauts work when logging in after signing up with the * client (#4002) * blacklisting common system email addresses listed in RFC 2142 (#3602) * blacklisting all usernames on the server (#3602) * configurable blocking of logins in the configuration (#3602) * require aliases to be all lower-case * Only allow braintree one-off payments when unauthenticated, & call them 'donations'. (#3796) --- lib/leap_web/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leap_web/version.rb b/lib/leap_web/version.rb index a55c2ca..009ee29 100644 --- a/lib/leap_web/version.rb +++ b/lib/leap_web/version.rb @@ -1,3 +1,3 @@ module LeapWeb - VERSION = "0.2.4" unless defined?(LeapWeb::VERSION) + VERSION = "0.2.5" unless defined?(LeapWeb::VERSION) end -- cgit v1.2.3 From 18c6f005d0eb0099a1d41235818564fbab94fb1b Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 18 Oct 2013 12:20:23 +0200 Subject: test logging in through the API using python with umlauts --- users/test/integration/api/python/umlauts.py | 79 ++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 users/test/integration/api/python/umlauts.py diff --git a/users/test/integration/api/python/umlauts.py b/users/test/integration/api/python/umlauts.py new file mode 100755 index 0000000..96fecbf --- /dev/null +++ b/users/test/integration/api/python/umlauts.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# coding: utf-8 + +# under development + +import requests +import json +import string +import random +import srp._pysrp as srp +import binascii + +safe_unhexlify = lambda x: binascii.unhexlify(x) if (len(x) % 2 == 0) else binascii.unhexlify('0'+x) + +# using globals for now +# server = 'https://dev.bitmask.net/1' +server = 'http://api.lvh.me:3000/1' + +def run_tests(): + login = 'test_' + id_generator() + password = id_generator() + "äöì" + id_generator() + usr = srp.User( login, password, srp.SHA256, srp.NG_1024 ) + print_and_parse(signup(login, password)) + + auth = print_and_parse(authenticate(usr)) + verify_or_debug(auth, usr) + assert usr.authenticated() + + +# let's have some random name +def id_generator(size=6, chars=string.ascii_lowercase + string.digits): + return ''.join(random.choice(chars) for x in range(size)) + +# log the server communication +def print_and_parse(response): + request = response.request + print request.method + ': ' + response.url + if hasattr(request, 'data'): + print " " + json.dumps(response.request.data) + print " -> " + response.text + try: + return json.loads(response.text) + except ValueError: + return None + +def signup(login, password): + salt, vkey = srp.create_salted_verification_key( login, password, srp.SHA256, srp.NG_1024 ) + user_params = { + 'user[login]': login, + 'user[password_verifier]': binascii.hexlify(vkey), + 'user[password_salt]': binascii.hexlify(salt) + } + print json.dumps(user_params) + return requests.post(server + '/users.json', data = user_params, verify = False) + +def authenticate(usr): + session = requests.session() + uname, A = usr.start_authentication() + params = { + 'login': uname, + 'A': binascii.hexlify(A) + } + init = print_and_parse(session.post(server + '/sessions', data = params, verify=False)) + M = usr.process_challenge( safe_unhexlify(init['salt']), safe_unhexlify(init['B']) ) + return session.put(server + '/sessions/' + uname, verify = False, + data = {'client_auth': binascii.hexlify(M)}) + +def verify_or_debug(auth, usr): + if ( 'errors' in auth ): + print ' u = "%x"' % usr.u + print ' x = "%x"' % usr.x + print ' v = "%x"' % usr.v + print ' S = "%x"' % usr.S + print ' K = "' + binascii.hexlify(usr.K) + '"' + print ' M = "' + binascii.hexlify(usr.M) + '"' + else: + usr.verify_session( safe_unhexlify(auth["M2"]) ) + +run_tests() -- cgit v1.2.3 From 936015afe051c82d5677601f7f58944ed42b4623 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 18 Oct 2013 13:12:50 +0200 Subject: use https sources in Gemfiles and also in the documentation (#4109) --- Gemfile | 2 +- INSTALL.md | 4 ++-- billing/Gemfile | 2 +- certs/Gemfile | 2 +- config/deploy.rb.example | 2 +- core/Gemfile | 2 +- help/Gemfile | 2 +- users/Gemfile | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 8ca0eaf..a346afa 100644 --- a/Gemfile +++ b/Gemfile @@ -29,4 +29,4 @@ group :test do end # unreleased so far ... but leap_web_certs need it -gem 'certificate_authority', :git => 'git://github.com/cchandler/certificate_authority.git' +gem 'certificate_authority', :git => 'https://github.com/cchandler/certificate_authority.git' diff --git a/INSTALL.md b/INSTALL.md index 7e95b76..75cb2a6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -8,7 +8,7 @@ Install git, ruby 1.9, rubygems and couchdb on your system. Then run ``` gem install bundler -git clone git://github.com/leapcode/leap_web.git +git clone https://leap.se/git/leap_web cd leap_web git submodule init git submodule update @@ -36,7 +36,7 @@ The following packages need to be installed: Simply clone the git repository: ``` - git clone git://github.com/leapcode/leap_web.git + git clone https://leap.se/git/leap_web cd leap_web ``` diff --git a/billing/Gemfile b/billing/Gemfile index 68ea51b..30e9669 100644 --- a/billing/Gemfile +++ b/billing/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) eval(File.read(File.dirname(__FILE__) + '/../ui_dependencies.rb')) diff --git a/certs/Gemfile b/certs/Gemfile index 951d1b7..992f236 100644 --- a/certs/Gemfile +++ b/certs/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) diff --git a/config/deploy.rb.example b/config/deploy.rb.example index 9e54c22..1fd4b8c 100644 --- a/config/deploy.rb.example +++ b/config/deploy.rb.example @@ -3,7 +3,7 @@ require "bundler/capistrano" set :application, "webapp" set :scm, :git -set :repository, "git://leap.se/leap_web" +set :repository, "https://leap.se/git/leap_web" set :branch, "master" set :deploy_via, :remote_cache diff --git a/core/Gemfile b/core/Gemfile index 52ed377..b552dc5 100644 --- a/core/Gemfile +++ b/core/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" # Declare your gem's dependencies in leap_web_core.gemspec. # Bundler will treat runtime dependencies like base dependencies, and diff --git a/help/Gemfile b/help/Gemfile index 5e895e9..ad7d29b 100644 --- a/help/Gemfile +++ b/help/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) eval(File.read(File.dirname(__FILE__) + '/..//ui_dependencies.rb')) diff --git a/users/Gemfile b/users/Gemfile index e30033a..4101ead 100644 --- a/users/Gemfile +++ b/users/Gemfile @@ -1,4 +1,4 @@ -source "http://rubygems.org" +source "https://rubygems.org" eval(File.read(File.dirname(__FILE__) + '/../common_dependencies.rb')) eval(File.read(File.dirname(__FILE__) + '/../ui_dependencies.rb')) -- cgit v1.2.3 From b8cb983d502f3c801c8ff801cb44f51e0d2ded4c Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 18 Oct 2013 13:47:09 +0200 Subject: include Gemfile.lock to prevent unintended updates (#4174) We had a broken production server lately because it had upgraded the couchrest model dependency to one that had a different naming scheme for a function we overwrite. So that broke production. Let's prevent that by including the Gemfile.lock in our repositories. --- .gitignore | 1 - Gemfile.lock | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index 84acd8d..f65233f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ /pkg /*/pkg /log -Gemfile.lock */Gemfile.lock test/dummy/log/* test/dummy/tmp/* diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..8632805 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,288 @@ +GIT + remote: https://github.com/cchandler/certificate_authority.git + revision: 58161e4552cc1aeca846da3e25ed66721354ee11 + specs: + certificate_authority (0.2.0) + activemodel (>= 3.0.6) + +PATH + remote: billing + specs: + leap_web_billing (0.2.5) + braintree + leap_web_core (= 0.2.5) + +PATH + remote: certs + specs: + leap_web_certs (0.2.5) + certificate_authority (>= 0.2.0) + leap_web_core (= 0.2.5) + +PATH + remote: core + specs: + leap_web_core (0.2.5) + couchrest (~> 1.1.3) + couchrest_model (~> 2.0.0) + couchrest_session_store (~> 0.2.0) + json + rails (~> 3.2.11) + +PATH + remote: help + specs: + leap_web_help (0.2.5) + leap_web_core (= 0.2.5) + +PATH + remote: users + specs: + leap_web_users (0.2.5) + leap_web_core (= 0.2.5) + rails_warden + ruby-srp (~> 0.2.1) + +GEM + remote: https://rubygems.org/ + remote: http://rubygems.org/ + specs: + SyslogLogger (2.0) + actionmailer (3.2.15) + actionpack (= 3.2.15) + mail (~> 2.5.4) + actionpack (3.2.15) + activemodel (= 3.2.15) + activesupport (= 3.2.15) + builder (~> 3.0.0) + erubis (~> 2.7.0) + journey (~> 1.0.4) + rack (~> 1.4.5) + rack-cache (~> 1.2) + rack-test (~> 0.6.1) + sprockets (~> 2.2.1) + activemodel (3.2.15) + activesupport (= 3.2.15) + builder (~> 3.0.0) + activerecord (3.2.15) + activemodel (= 3.2.15) + activesupport (= 3.2.15) + arel (~> 3.0.2) + tzinfo (~> 0.3.29) + activeresource (3.2.15) + activemodel (= 3.2.15) + activesupport (= 3.2.15) + activesupport (3.2.15) + i18n (~> 0.6, >= 0.6.4) + multi_json (~> 1.0) + addressable (2.3.5) + arel (3.0.2) + bootstrap-sass (2.1.1.0) + bootswatch-rails (0.5.0) + railties (>= 3.1) + braintree (2.25.0) + builder (>= 2.0.0) + builder (3.0.4) + capybara (2.1.0) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + client_side_validations (3.2.6) + client_side_validations-simple_form (2.1.0) + client_side_validations (~> 3.2.5) + simple_form (~> 2.1.0) + cliver (0.2.2) + coffee-rails (3.2.2) + coffee-script (>= 2.2.0) + railties (~> 3.2.0) + coffee-script (2.2.0) + coffee-script-source + execjs + coffee-script-source (1.6.3) + columnize (0.3.6) + couchrest (1.1.3) + mime-types (~> 1.15) + multi_json (~> 1.0) + rest-client (~> 1.6.1) + couchrest_model (2.0.0) + activemodel (>= 3.0) + couchrest (~> 1.1.3) + mime-types (>= 1.15) + tzinfo (>= 0.3.22) + couchrest_session_store (0.2.0) + actionpack + couchrest + couchrest_model + daemons (1.1.9) + debugger (1.6.2) + columnize (>= 0.3.1) + debugger-linecache (~> 1.2.0) + debugger-ruby_core_source (~> 1.2.3) + debugger-linecache (1.2.0) + debugger-ruby_core_source (1.2.3) + erubis (2.7.0) + eventmachine (1.0.3) + execjs (2.0.2) + factory_girl (4.2.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.2.1) + factory_girl (~> 4.2.0) + railties (>= 3.0.0) + fake_braintree (0.4) + activesupport + braintree (~> 2.5) + capybara + i18n + sinatra + thin + faker (1.2.0) + i18n (~> 0.5) + haml (3.1.8) + haml-rails (0.3.5) + actionpack (>= 3.1, < 4.1) + activesupport (>= 3.1, < 4.1) + haml (~> 3.1) + railties (>= 3.1, < 4.1) + hike (1.2.3) + i18n (0.6.5) + journey (1.0.4) + jquery-rails (3.0.4) + railties (>= 3.0, < 5.0) + thor (>= 0.14, < 2.0) + json (1.8.1) + kaminari (0.13.0) + actionpack (>= 3.0.0) + activesupport (>= 3.0.0) + railties (>= 3.0.0) + launchy (2.3.0) + addressable (~> 2.3) + libv8 (3.3.10.4) + mail (2.5.4) + mime-types (~> 1.16) + treetop (~> 1.4.8) + metaclass (0.0.1) + mime-types (1.25) + mini_portile (0.5.1) + mocha (0.13.3) + metaclass (~> 0.0.1) + multi_json (1.8.2) + nokogiri (1.6.0) + mini_portile (~> 0.5.0) + poltergeist (1.4.1) + capybara (~> 2.1.0) + cliver (~> 0.2.1) + multi_json (~> 1.0) + websocket-driver (>= 0.2.0) + polyglot (0.3.3) + quiet_assets (1.0.2) + railties (>= 3.1, < 5.0) + rack (1.4.5) + rack-cache (1.2) + rack (>= 0.4) + rack-protection (1.5.0) + rack + rack-ssl (1.3.3) + rack + rack-test (0.6.2) + rack (>= 1.0) + rails (3.2.15) + actionmailer (= 3.2.15) + actionpack (= 3.2.15) + activerecord (= 3.2.15) + activeresource (= 3.2.15) + activesupport (= 3.2.15) + bundler (~> 1.0) + railties (= 3.2.15) + rails-i18n (3.0.0) + i18n (~> 0.5) + rails (>= 3.0.0, < 4.0.0) + rails_warden (0.5.7) + warden (>= 1.0.0) + railties (3.2.15) + actionpack (= 3.2.15) + activesupport (= 3.2.15) + rack-ssl (~> 1.3.2) + rake (>= 0.8.7) + rdoc (~> 3.4) + thor (>= 0.14.6, < 2.0) + rake (10.1.0) + rdoc (3.12.2) + json (~> 1.4) + rest-client (1.6.7) + mime-types (>= 1.16) + ruby-srp (0.2.1) + sass (3.2.12) + sass-rails (3.2.6) + railties (~> 3.2.0) + sass (>= 3.1.10) + tilt (~> 1.3) + simple_form (2.1.0) + actionpack (~> 3.0) + activemodel (~> 3.0) + sinatra (1.4.3) + rack (~> 1.4) + rack-protection (~> 1.4) + tilt (~> 1.3, >= 1.3.4) + sprockets (2.2.2) + hike (~> 1.2) + multi_json (~> 1.0) + rack (~> 1.0) + tilt (~> 1.1, != 1.3.0) + therubyracer (0.10.2) + libv8 (~> 3.3.10) + thin (1.5.1) + daemons (>= 1.0.9) + eventmachine (>= 0.12.6) + rack (>= 1.0.0) + thor (0.18.1) + tilt (1.4.1) + treetop (1.4.15) + polyglot + polyglot (>= 0.3.1) + tzinfo (0.3.38) + uglifier (1.2.7) + execjs (>= 0.3.0) + multi_json (~> 1.3) + warden (1.2.3) + rack (>= 1.0) + websocket-driver (0.3.0) + xpath (2.0.0) + nokogiri (~> 1.3) + +PLATFORMS + ruby + +DEPENDENCIES + SyslogLogger (~> 2.0) + bootstrap-sass (~> 2.1.0) + bootswatch-rails (~> 0.5.0) + capybara + certificate_authority! + client_side_validations + client_side_validations-simple_form + coffee-rails (~> 3.2.2) + debugger + factory_girl_rails + fake_braintree + faker + haml (~> 3.1.7) + haml-rails (~> 0.3.4) + jquery-rails + kaminari (= 0.13.0) + launchy + leap_web_billing! + leap_web_certs! + leap_web_core! + leap_web_help! + leap_web_users! + mocha (~> 0.13.0) + poltergeist + quiet_assets + rails-i18n + sass-rails (~> 3.2.5) + simple_form + therubyracer (~> 0.10.2) + thin + uglifier (~> 1.2.7) -- cgit v1.2.3 From 3389bb0f4e43087ef1bf3073c1cb2e8cf64b60d1 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 21 Oct 2013 11:30:18 +0200 Subject: fix download urls for mac, android and windows They did not point directly to the download. --- config/defaults.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/defaults.yml b/config/defaults.yml index 6211e37..4d0a8d8 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -22,12 +22,12 @@ downloads: &downloads - windows - android download_paths: - android: /client/android + android: /client/android/Bitmask-Android-latest.apk linux: /client/linux linux32: /client/linux/Bitmask-linux32-latest.tar.bz2 linux64: /client/linux/Bitmask-linux64-latest.tar.bz2 - osx: /client/osx/Bitmask-OSC-latest.dmg - windows: /client/windows + mac: /client/osx/Bitmask-OSX-latest.dmg + windows: /client/windows/Bitmask-win32-latest.zip other: /client common: &common -- cgit v1.2.3 From 12f3d31e14fed756e909c8a656b00f92a9a62234 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 21 Oct 2013 11:51:36 +0200 Subject: remove duplicate source line that was using http I don't think we need to list the sources again in common_dependencies.rb --- Gemfile.lock | 1 - common_dependencies.rb | 2 -- 2 files changed, 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8632805..e6096fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,7 +45,6 @@ PATH GEM remote: https://rubygems.org/ - remote: http://rubygems.org/ specs: SyslogLogger (2.0) actionmailer (3.2.15) diff --git a/common_dependencies.rb b/common_dependencies.rb index 6a43e26..2225613 100644 --- a/common_dependencies.rb +++ b/common_dependencies.rb @@ -1,5 +1,3 @@ -source "http://rubygems.org" - group :test do # moching and stubing gem 'mocha', '~> 0.13.0', :require => false -- cgit v1.2.3 From 23b9c58c4bd2e62ba63064c0e606d84f26fe74fa Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 21 Oct 2013 18:18:13 +0200 Subject: use osx not mac as an identifier for the os. --- config/defaults.yml | 4 ++-- core/config/locales/en.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/defaults.yml b/config/defaults.yml index 4d0a8d8..c7c8502 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -18,7 +18,7 @@ downloads: &downloads available_clients: - linux32 - linux64 - - mac + - osx - windows - android download_paths: @@ -26,7 +26,7 @@ downloads: &downloads linux: /client/linux linux32: /client/linux/Bitmask-linux32-latest.tar.bz2 linux64: /client/linux/Bitmask-linux64-latest.tar.bz2 - mac: /client/osx/Bitmask-OSX-latest.dmg + osx: /client/osx/Bitmask-OSX-latest.dmg windows: /client/windows/Bitmask-win32-latest.zip other: /client diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 344eee4..4710a16 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -35,6 +35,6 @@ en: linux64: "Linux (64 bit)" windows: "Windows" android: "Android" - mac: "Mac OSX" + osx: "Mac OSX" other: "(not available for your OS.)" -- cgit v1.2.3 From 9cdd17f630f6e0399e841117f4d6fc98cd437bb8 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 21 Oct 2013 18:30:19 +0200 Subject: adopt css to mac-> osx change --- app/assets/stylesheets/leap.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/leap.scss b/app/assets/stylesheets/leap.scss index 30f7741..af3bde4 100644 --- a/app/assets/stylesheets/leap.scss +++ b/app/assets/stylesheets/leap.scss @@ -78,11 +78,11 @@ html.windows .os-windows { display: inherit !important; } -.os-mac { +.os-osx { display: none !important; } -html.mac .os-mac { +html.osx .os-osx { display: inherit !important; } -- cgit v1.2.3 From 7b3dd8c2c0436518c7c80d37498b57fcf3b1eaf8 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 21 Oct 2013 18:55:26 +0200 Subject: the class for the html tag is linux32 - not just linux --- app/assets/stylesheets/leap.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/leap.scss b/app/assets/stylesheets/leap.scss index af3bde4..abfea05 100644 --- a/app/assets/stylesheets/leap.scss +++ b/app/assets/stylesheets/leap.scss @@ -58,7 +58,7 @@ html.android .os-android { display: none !important; } -html.linux .os-linux32 { +html.linux32 .os-linux32 { display: inherit !important; } -- cgit v1.2.3 From 315a7c9aa3d6cc6cde51a67b6dcc91aea085f518 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 28 Oct 2013 10:48:53 +0100 Subject: reset button loading... state on error (#4231) including test refactored error display some --- users/app/assets/javascripts/users.js | 51 ++++++++++++++++++-------- users/test/integration/browser/account_test.rb | 14 +++++++ 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/users/app/assets/javascripts/users.js b/users/app/assets/javascripts/users.js index aaeba6e..8486756 100644 --- a/users/app/assets/javascripts/users.js +++ b/users/app/assets/javascripts/users.js @@ -46,6 +46,13 @@ $(form).find('input[type="submit"]').button('loading'); }; + resetButtons = function(submitEvent) { + var form = $('form.submitted') + // bootstrap loading state: + $(form).find('input[type="submit"]').button('reset'); + $(form).removeClass('submitted') + }; + // // PUBLIC FUNCTIONS // @@ -70,24 +77,36 @@ // srp.error = function(message) { clear_errors(); - var element, error, field; + var errors = extractErrors(message); + displayErrors(errors); + resetButtons(); + } + + function extractErrors(message) { if ($.isPlainObject(message) && message.errors) { - for (field in message.errors) { - if (field == 'base') { - alert_message(message.errors[field]); - continue; - } - error = message.errors[field]; - element = $('form input[name$="[' + field + ']"]'); - if (!element) { - continue; - } - element.trigger('element:validate:fail.ClientSideValidations', error).data('valid', false); - } - } else if (message.error) { - alert_message(message.error); + return message.errors; } else { - alert_message(JSON.stringify(message)); + return { + base: (message.error || JSON.stringify(message)) + }; + } + } + + function displayErrors(errors) { + for (var field in errors) { + var error = errors[field]; + if (field === 'base') { + alert_message(error); + } else { + displayFieldError(field, error); + } + } + } + + function displayFieldError(field, error) { + var element = $('form input[name$="[' + field + ']"]'); + if (element) { + element.trigger('element:validate:fail.ClientSideValidations', error).data('valid', false); } }; diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index 1deda45..243ccfb 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -27,6 +27,20 @@ class AccountTest < BrowserIntegrationTest User.find_by_login(username).account.destroy end + test "failed login" do + username, password = submit_signup + click_on 'Logout' + click_on 'Log In' + fill_in 'Username', with: username + fill_in 'Password', with: "wrong password" + click_on 'Log In' + assert page.has_selector? 'input.btn-primary.disabled' + assert page.has_content? I18n.t(:invalid_user_pass) + assert page.has_no_content?("Welcome #{username}") + assert page.has_no_selector? 'input.btn-primary.disabled' + User.find_by_login(username).account.destroy + end + test "change password" do username, password = submit_signup click_on "Account Settings" -- cgit v1.2.3 From 1ef3e9df271934b983ff5afe60c2dcf34c090a98 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 28 Oct 2013 11:08:31 +0100 Subject: no need to create a user for testing failed login attempt --- users/test/integration/browser/account_test.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index 243ccfb..8e03856 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -28,17 +28,14 @@ class AccountTest < BrowserIntegrationTest end test "failed login" do - username, password = submit_signup - click_on 'Logout' + visit '/' click_on 'Log In' - fill_in 'Username', with: username + fill_in 'Username', with: "username" fill_in 'Password', with: "wrong password" click_on 'Log In' assert page.has_selector? 'input.btn-primary.disabled' assert page.has_content? I18n.t(:invalid_user_pass) - assert page.has_no_content?("Welcome #{username}") assert page.has_no_selector? 'input.btn-primary.disabled' - User.find_by_login(username).account.destroy end test "change password" do -- cgit v1.2.3 From 7aaedeaf6fdd2d84ebab7bde2f6a6bdcf8d930b8 Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 28 Oct 2013 13:10:12 -0700 Subject: Fix button to enable account: https://leap.se/code/issues/4246 --- users/app/views/users/_edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index 9d2473b..b86172e 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -63,4 +63,4 @@ %p= t(:enable_description) = link_to enable_user_path(@user), :method => :post, :class => "btn btn-warning" do %i.icon-ok.icon-white - = t(:enable) + = t(:enable) -- cgit v1.2.3 From dd88c7f84cb3c497c6327c364b3c08993c51a08f Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 28 Oct 2013 12:47:46 +0100 Subject: notify user their account was successfully deleted (refs #4216) Also fixes a cornercase when admins deleted their own account. So far they would be redirected to the users list - which then refused access. Now they'll be redirected to the home landing page as well. --- app/views/home/index.html.haml | 4 ++++ users/app/controllers/users_controller.rb | 10 +++++++++- users/config/locales/en.yml | 1 + users/test/integration/browser/account_test.rb | 8 ++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 728b5b8..5a54354 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -6,6 +6,10 @@ %p We provide secure communication services, including encrypted internet, email (coming soon), and chat (coming later). + .row-fluid + .span6.offset3 + = render 'layouts/messages' + .row-fluid = home_page_buttons - if Rails.env == 'development' diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index f66277d..de21983 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -48,7 +48,15 @@ class UsersController < UsersBaseController def destroy @user.destroy - redirect_to admin? ? users_url : root_url + flash[:notice] = I18n.t(:account_destroyed) + # admins can destroy other users + if @user != current_user + redirect_to users_url + else + # let's remove the invalid session + logout + redirect_to root_url + end end end diff --git a/users/config/locales/en.yml b/users/config/locales/en.yml index b69f7f4..1b5dd5e 100644 --- a/users/config/locales/en.yml +++ b/users/config/locales/en.yml @@ -17,6 +17,7 @@ en: destroy_my_account: "Destroy my account" destroy_account_info: "This will permanently destroy your account and all the data associated with it. Proceed with caution!" admin_destroy_account: "Destroy the account %{username}" + account_destroyed: "The account has been destroyed successfully." set_email_address: "Set email address" forward_email: "Forward Email" email_aliases: "Email Aliases" diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index 8e03856..b712c95 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -38,6 +38,14 @@ class AccountTest < BrowserIntegrationTest assert page.has_no_selector? 'input.btn-primary.disabled' end + test "account destruction" do + username, password = submit_signup + click_on I18n.t('account_settings') + click_on I18n.t('destroy_my_account') + page.save_screenshot('/tmp/destroy.png') + assert page.has_content?(I18n.t('account_destroyed')) + end + test "change password" do username, password = submit_signup click_on "Account Settings" -- cgit v1.2.3 From f6924bfe3b540c384fa53e55db9db3a64a34ced3 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 30 Oct 2013 20:13:16 +0100 Subject: test helper to expect_logout. Currently it expects both the session and the token to be cleared. This might change. But we'll always have a definition of what it means to logout we can test this way. --- users/test/functional/sessions_controller_test.rb | 14 +++----------- users/test/functional/users_controller_test.rb | 1 + users/test/functional/v1/sessions_controller_test.rb | 19 ++----------------- users/test/support/auth_test_helper.rb | 14 ++++++++++++++ 4 files changed, 20 insertions(+), 28 deletions(-) diff --git a/users/test/functional/sessions_controller_test.rb b/users/test/functional/sessions_controller_test.rb index a630e6e..28143da 100644 --- a/users/test/functional/sessions_controller_test.rb +++ b/users/test/functional/sessions_controller_test.rb @@ -41,20 +41,12 @@ class SessionsControllerTest < ActionController::TestCase assert_json_error :login => I18n.t(:all_strategies_failed) end - test "logout should reset warden user" do - expect_warden_logout + test "destory should logout" do + login + expect_logout delete :destroy assert_response :redirect assert_redirected_to root_url end - def expect_warden_logout - raw = mock('raw session') do - expects(:inspect) - end - request.env['warden'].expects(:raw_session).returns(raw) - request.env['warden'].expects(:logout) - end - - end diff --git a/users/test/functional/users_controller_test.rb b/users/test/functional/users_controller_test.rb index 052de04..75d900f 100644 --- a/users/test/functional/users_controller_test.rb +++ b/users/test/functional/users_controller_test.rb @@ -91,6 +91,7 @@ class UsersControllerTest < ActionController::TestCase user.expects(:destroy) login user + expect_logout delete :destroy, :id => @current_user.id assert_response :redirect diff --git a/users/test/functional/v1/sessions_controller_test.rb b/users/test/functional/v1/sessions_controller_test.rb index ff9fca1..4200e8f 100644 --- a/users/test/functional/v1/sessions_controller_test.rb +++ b/users/test/functional/v1/sessions_controller_test.rb @@ -52,26 +52,11 @@ class V1::SessionsControllerTest < ActionController::TestCase assert_equal @user.id, token.user_id end - test "logout should reset session" do - expect_warden_logout - delete :destroy - assert_response 204 - end - - test "logout should destroy token" do + test "destroy should logout" do login - expect_warden_logout - @token.expects(:destroy) + expect_logout delete :destroy assert_response 204 end - def expect_warden_logout - raw = mock('raw session') do - expects(:inspect) - end - request.env['warden'].expects(:raw_session).returns(raw) - request.env['warden'].expects(:logout) - end - end diff --git a/users/test/support/auth_test_helper.rb b/users/test/support/auth_test_helper.rb index 609f115..50e9453 100644 --- a/users/test/support/auth_test_helper.rb +++ b/users/test/support/auth_test_helper.rb @@ -38,12 +38,26 @@ module AuthTestHelper end end + def expect_logout + expect_warden_logout + @token.expects(:destroy) if @token + end + protected def header_for_token_auth @token = find_record(:token, :authenticate => @current_user) ActionController::HttpAuthentication::Token.encode_credentials @token.id end + + def expect_warden_logout + raw = mock('raw session') do + expects(:inspect) + end + request.env['warden'].expects(:raw_session).returns(raw) + request.env['warden'].expects(:logout) + end + end class ActionController::TestCase -- cgit v1.2.3 From 7e93258f552d6fd1114626561e6393aa483228fe Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 4 Nov 2013 12:20:07 +0100 Subject: Version 0.2.6 * reset button state from 'loading...' after failed login attempt (#4231) * use https sources in Gemfiles and documentation(#4109) * include Gemfile.lock to prevent unintended updates (#4174) * fixed download urls to get latest versions for mac, android and windows * test api login with umlauts in password --- lib/leap_web/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leap_web/version.rb b/lib/leap_web/version.rb index 009ee29..0ac6e09 100644 --- a/lib/leap_web/version.rb +++ b/lib/leap_web/version.rb @@ -1,3 +1,3 @@ module LeapWeb - VERSION = "0.2.5" unless defined?(LeapWeb::VERSION) + VERSION = "0.2.6" unless defined?(LeapWeb::VERSION) end -- cgit v1.2.3 From b4ca13257341792f5e6496264c421af1888bcdb8 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 5 Nov 2013 11:40:25 +0100 Subject: refactor: Identity.disable_all_for(user) on user destruction This way the identity model defines how identities should be disabled. We currently still destroy them. But it will be easy and nicely isolated to change this next. --- users/app/models/account.rb | 4 +--- users/app/models/identity.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/users/app/models/account.rb b/users/app/models/account.rb index 5368a1b..726f642 100644 --- a/users/app/models/account.rb +++ b/users/app/models/account.rb @@ -29,9 +29,7 @@ class Account def destroy return unless @user - Identity.by_user_id.key(@user.id).each do |identity| - identity.destroy - end + Identity.disable_all_for(@user) @user.destroy end diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb index e0a24e9..c24af73 100644 --- a/users/app/models/identity.rb +++ b/users/app/models/identity.rb @@ -50,6 +50,12 @@ class Identity < CouchRest::Model::Base identity end + def self.disable_all_for(user) + Identity.by_user_id.key(user.id).each do |identity| + identity.disable + end + end + def self.attributes_from_user(user) { user_id: user.id, address: user.email_address, @@ -57,6 +63,12 @@ class Identity < CouchRest::Model::Base } end + # + # about to change towards actually disabling the identity instead of + # destroying it. + # + alias_method :disable, :destroy + def keys read_attribute('keys') || HashWithIndifferentAccess.new end -- cgit v1.2.3 From 99ecdbf71632970d4c83f99beea325e5d213e4c6 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 5 Nov 2013 12:12:13 +0100 Subject: disabled identities to block handles after a user was deleted --- users/app/models/identity.rb | 17 +++++++++++------ users/test/unit/account_test.rb | 3 ++- users/test/unit/identity_test.rb | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb index c24af73..40ce4ae 100644 --- a/users/app/models/identity.rb +++ b/users/app/models/identity.rb @@ -53,6 +53,7 @@ class Identity < CouchRest::Model::Base def self.disable_all_for(user) Identity.by_user_id.key(user.id).each do |identity| identity.disable + identity.save end end @@ -63,11 +64,14 @@ class Identity < CouchRest::Model::Base } end - # - # about to change towards actually disabling the identity instead of - # destroying it. - # - alias_method :disable, :destroy + def enabled? + self.destination && self.user_id + end + + def disable + self.destination = nil + self.user_id = nil + end def keys read_attribute('keys') || HashWithIndifferentAccess.new @@ -105,7 +109,8 @@ class Identity < CouchRest::Model::Base end def destination_email - return if destination.valid? #this ensures it is Email + return if destination.nil? # this identity is disabled + return if destination.valid? # this ensures it is Email self.errors.add(:destination, destination.errors.messages[:email].first) #assumes only one error #TODO end diff --git a/users/test/unit/account_test.rb b/users/test/unit/account_test.rb index 94a9980..a8c6efd 100644 --- a/users/test/unit/account_test.rb +++ b/users/test/unit/account_test.rb @@ -13,7 +13,8 @@ class AccountTest < ActiveSupport::TestCase end test "create and remove a user account" do - assert_no_difference "Identity.count" do + # We keep an identity that will block the handle from being reused. + assert_difference "Identity.count" do assert_no_difference "User.count" do user = Account.create(FactoryGirl.attributes_for(:user)) user.account.destroy diff --git a/users/test/unit/identity_test.rb b/users/test/unit/identity_test.rb index 0842a77..8270689 100644 --- a/users/test/unit/identity_test.rb +++ b/users/test/unit/identity_test.rb @@ -90,6 +90,26 @@ class IdentityTest < ActiveSupport::TestCase assert id.errors.messages[:destination].include? "needs to be a valid email address" end + test "disabled identity" do + id = Identity.for(@user) + id.disable + assert_equal @user.email_address, id.address + assert_equal nil, id.destination + assert_equal nil, id.user + assert !id.enabled? + assert id.valid? + end + + test "disabled identity blocks handle" do + id = Identity.for(@user) + id.disable + id.save + other_user = find_record :user + taken = Identity.build_for other_user, address: id.address + assert !taken.valid? + id.destroy + end + def alias_name @alias_name ||= Faker::Internet.user_name end -- cgit v1.2.3 From 4a2490cc5eac1803be80fade65bbe9d32fa0bd9b Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 5 Nov 2013 17:03:55 +0100 Subject: Identity.destroy_all_disabled will clean up disabled identities This is mostly for cleaning up after tests so far. But we might expand this to destroy all identities disabled before a certain date. --- users/app/models/identity.rb | 17 +++++++++++++++++ users/test/unit/account_test.rb | 4 ++++ users/test/unit/identity_test.rb | 11 ++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb index 40ce4ae..97966d0 100644 --- a/users/app/models/identity.rb +++ b/users/app/models/identity.rb @@ -27,6 +27,17 @@ class Identity < CouchRest::Model::Base emit(doc.address, doc.keys["pgp"]); } EOJS + view :disabled, + map: <<-EOJS + function(doc) { + if (doc.type != 'Identity') { + return; + } + if (typeof doc.user_id === "undefined") { + emit(doc._id, 1); + } + } + EOJS end @@ -57,6 +68,12 @@ class Identity < CouchRest::Model::Base end end + def self.destroy_all_disabled + Identity.disabled.each do |identity| + identity.destroy + end + end + def self.attributes_from_user(user) { user_id: user.id, address: user.email_address, diff --git a/users/test/unit/account_test.rb b/users/test/unit/account_test.rb index a8c6efd..4fb3c3d 100644 --- a/users/test/unit/account_test.rb +++ b/users/test/unit/account_test.rb @@ -2,6 +2,10 @@ require 'test_helper' class AccountTest < ActiveSupport::TestCase + teardown do + Identity.destroy_all_disabled + end + test "create a new account" do user = Account.create(FactoryGirl.attributes_for(:user)) assert user.valid? diff --git a/users/test/unit/identity_test.rb b/users/test/unit/identity_test.rb index 8270689..78ef52c 100644 --- a/users/test/unit/identity_test.rb +++ b/users/test/unit/identity_test.rb @@ -107,7 +107,16 @@ class IdentityTest < ActiveSupport::TestCase other_user = find_record :user taken = Identity.build_for other_user, address: id.address assert !taken.valid? - id.destroy + Identity.destroy_all_disabled + end + + test "destroy all disabled identities" do + id = Identity.for(@user) + id.disable + id.save + assert Identity.count > 0 + Identity.destroy_all_disabled + assert_equal 0, Identity.count end def alias_name -- cgit v1.2.3 From 40f24e2887672957acf7ecedce58e692cc9505ca Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 6 Nov 2013 10:10:44 +0100 Subject: refactor: extract method on account test also test one can't login anymore after destroying the account. --- users/test/integration/browser/account_test.rb | 35 ++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index b712c95..6e9aab5 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -19,31 +19,24 @@ class AccountTest < BrowserIntegrationTest test "successful login" do username, password = submit_signup click_on 'Logout' - click_on 'Log In' - fill_in 'Username', with: username - fill_in 'Password', with: password - click_on 'Log In' + attempt_login(username, password) assert page.has_content?("Welcome #{username}") User.find_by_login(username).account.destroy end test "failed login" do visit '/' - click_on 'Log In' - fill_in 'Username', with: "username" - fill_in 'Password', with: "wrong password" - click_on 'Log In' - assert page.has_selector? 'input.btn-primary.disabled' - assert page.has_content? I18n.t(:invalid_user_pass) - assert page.has_no_selector? 'input.btn-primary.disabled' + attempt_login("username", "wrong password") + assert_invalid_login(page) end test "account destruction" do username, password = submit_signup click_on I18n.t('account_settings') click_on I18n.t('destroy_my_account') - page.save_screenshot('/tmp/destroy.png') assert page.has_content?(I18n.t('account_destroyed')) + attempt_login(username, password) + assert_invalid_login(page) end test "change password" do @@ -55,10 +48,7 @@ class AccountTest < BrowserIntegrationTest click_on 'Save' end click_on 'Logout' - click_on 'Log In' - fill_in 'Username', with: username - fill_in 'Password', with: "other password" - click_on 'Log In' + attempt_login(username, "other password") assert page.has_content?("Welcome #{username}") User.find_by_login(username).account.destroy end @@ -100,6 +90,19 @@ class AccountTest < BrowserIntegrationTest assert page.has_content?("server failed") end + def attempt_login(username, password) + click_on 'Log In' + fill_in 'Username', with: username + fill_in 'Password', with: password + click_on 'Log In' + end + + def assert_invalid_login(page) + assert page.has_selector? 'input.btn-primary.disabled' + assert page.has_content? I18n.t(:invalid_user_pass) + assert page.has_no_selector? 'input.btn-primary.disabled' + end + def inject_malicious_js page.execute_script <<-EOJS var calc = new srp.Calculate(); -- cgit v1.2.3 From 44d273fd03645af5e546133adf4e9906800d3d5f Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 6 Nov 2013 10:20:33 +0100 Subject: integration test for blocking handles after account destroyed has not been run yet. --- users/test/integration/browser/account_test.rb | 12 ++++++++++++ users/test/support/integration_test_helper.rb | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index 6e9aab5..b349489 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -6,6 +6,10 @@ class AccountTest < BrowserIntegrationTest Capybara.current_driver = Capybara.javascript_driver end + teardown do + Identity.destroy_all_disabled + end + test "normal account workflow" do username, password = submit_signup assert page.has_content?("Welcome #{username}") @@ -39,6 +43,14 @@ class AccountTest < BrowserIntegrationTest assert_invalid_login(page) end + test "handle blocked after account destruction" do + username, password = submit_signup + click_on I18n.t('account_settings') + click_on I18n.t('destroy_my_account') + submit_signup(username) + assert page.has_content?('has already been taken') + end + test "change password" do username, password = submit_signup click_on "Account Settings" diff --git a/users/test/support/integration_test_helper.rb b/users/test/support/integration_test_helper.rb index cfe72cf..51e47c6 100644 --- a/users/test/support/integration_test_helper.rb +++ b/users/test/support/integration_test_helper.rb @@ -1,7 +1,7 @@ module IntegrationTestHelper - def submit_signup - username = "test_#{SecureRandom.urlsafe_base64}".downcase - password = SecureRandom.base64 + def submit_signup(username = nil, password = nil) + username ||= "test_#{SecureRandom.urlsafe_base64}".downcase + password ||= SecureRandom.base64 visit '/users/new' fill_in 'Username', with: username fill_in 'Password', with: password -- cgit v1.2.3 From d620fd42925f1d3d29a66bb61a75bed8c2bdaf8f Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 6 Nov 2013 10:40:32 +0100 Subject: refactor: split up and cleaned up ticket validation tests --- help/test/factories.rb | 8 ++++++-- help/test/unit/ticket_test.rb | 41 +++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/help/test/factories.rb b/help/test/factories.rb index 5b38952..5368ce6 100644 --- a/help/test/factories.rb +++ b/help/test/factories.rb @@ -2,8 +2,12 @@ FactoryGirl.define do factory :ticket do title { Faker::Lorem.sentence } - comments_attributes do - { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } + email { Faker::Internet.email } + + factory :ticket_with_comment do + comments_attributes do + { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } + end end end diff --git a/help/test/unit/ticket_test.rb b/help/test/unit/ticket_test.rb index ce35e1d..04832d9 100644 --- a/help/test/unit/ticket_test.rb +++ b/help/test/unit/ticket_test.rb @@ -1,41 +1,38 @@ require 'test_helper' class TicketTest < ActiveSupport::TestCase - #test "the truth" do - # assert true - #end - setup do - @sample = Ticket.new + test "ticket with default attribs is valid" do + t = FactoryGirl.build :ticket + assert t.valid? end - test "validity" do - t = Ticket.create :title => 'test title', :email => 'blah@blah.com' + test "ticket without email is valid" do + t = FactoryGirl.build :ticket, email: "" assert t.valid? - assert_equal t.title, 'test title' + end + + test "ticket validates email format" do + t = FactoryGirl.build :ticket, email: "aswerssfd" + assert !t.valid? + end + test "ticket allows for multiple email addresses" do + t = FactoryGirl.build :ticket, email: 'blah@blah.com, bb@jjj.org' + assert !t.valid? + end + + test "ticket open states" do + t = FactoryGirl.build :ticket assert t.is_open t.close assert !t.is_open t.reopen assert t.is_open - #user = LeapWebHelp::User.new(User.valid_attributes_hash) - #user = LeapWebUsers::User.create - - #t.user = user - - #t.email = '' #invalid - #assert !t.valid? - #t.email = 'blah@blah.com, bb@jjj.org' - #assert t.valid? - t.email = 'bdlfjlkasfjklasjf' #invalid - #p t.email_address - #p t.email_address.strip =~ RFC822::EmailAddress - assert !t.valid? - t.reload.destroy end test "creation validated" do + @sample = Ticket.new assert !@sample.is_creator_validated? #p current_user @sample.created_by = 22 #current_user -- cgit v1.2.3 From ebc60f3aba1ca08e454ba5e91f49905df2e5fa13 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 6 Nov 2013 10:55:25 +0100 Subject: Ticket.destroy_all_from(user) - remove all tickets created by a user We'll use this to clean up after user destruction --- help/app/models/ticket.rb | 7 +++++++ help/test/factories.rb | 4 ++++ help/test/unit/ticket_test.rb | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/help/app/models/ticket.rb b/help/app/models/ticket.rb index 8066d0d..74f2050 100644 --- a/help/app/models/ticket.rb +++ b/help/app/models/ticket.rb @@ -24,6 +24,7 @@ class Ticket < CouchRest::Model::Base design do view :by_updated_at view :by_created_at + view :by_created_by view :by_is_open_and_created_at view :by_is_open_and_updated_at @@ -40,6 +41,12 @@ class Ticket < CouchRest::Model::Base @selection.tickets end + def self.destroy_all_from(user) + self.by_created_by.key(user.id).each do |ticket| + ticket.destroy + end + end + def is_creator_validated? !!created_by end diff --git a/help/test/factories.rb b/help/test/factories.rb index 5368ce6..bce3af1 100644 --- a/help/test/factories.rb +++ b/help/test/factories.rb @@ -9,6 +9,10 @@ FactoryGirl.define do { "0" => { "body" => Faker::Lorem.sentences.join(" ") } } end end + + factory :ticket_with_creator do + created_by { FactoryGirl.create(:user).id } + end end end diff --git a/help/test/unit/ticket_test.rb b/help/test/unit/ticket_test.rb index 04832d9..6c29d11 100644 --- a/help/test/unit/ticket_test.rb +++ b/help/test/unit/ticket_test.rb @@ -39,6 +39,12 @@ class TicketTest < ActiveSupport::TestCase assert @sample.is_creator_validated? end + test "destroy all tickets from a user" do + t = FactoryGirl.create :ticket_with_creator + u = t.created_by_user + Ticket.destroy_all_from(u) + assert_equal nil, Ticket.find(t.id) + end =begin # TODO: do once have current_user stuff in order test "code if & only if not creator-validated" do -- cgit v1.2.3 From 24598b5c5e4df20c423ec74ea8e9df1592483c6b Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 6 Nov 2013 11:26:46 +0100 Subject: destroy all tickets created by a user when account is destroyed In order to keep the users engine independent of the tickets engine i added a generic load hook to the account model. The tickets engine then monkeypatches the account destruction and destroys all tickets before the user is destroyed. The tickets are destroyed first so that even if things break there should never be tickets with an outdated user id. I would have prefered to use super over using an alias_method_chain but I have not been able to figure out a way to make account a superclass of the account extension and still refer to Account from the users engine. --- help/app/models/account_extension/tickets.rb | 13 +++++++++++++ help/config/initializers/account_lifecycle.rb | 3 +++ help/test/unit/account_extension_test.rb | 12 ++++++++++++ users/app/models/account.rb | 10 +++++++++- 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 help/app/models/account_extension/tickets.rb create mode 100644 help/config/initializers/account_lifecycle.rb create mode 100644 help/test/unit/account_extension_test.rb diff --git a/help/app/models/account_extension/tickets.rb b/help/app/models/account_extension/tickets.rb new file mode 100644 index 0000000..f898b56 --- /dev/null +++ b/help/app/models/account_extension/tickets.rb @@ -0,0 +1,13 @@ +module AccountExtension::Tickets + extend ActiveSupport::Concern + + def destroy_with_tickets + Ticket.destroy_all_from(self.user) + destroy_without_tickets + end + + included do + alias_method_chain :destroy, :tickets + end + +end diff --git a/help/config/initializers/account_lifecycle.rb b/help/config/initializers/account_lifecycle.rb new file mode 100644 index 0000000..d9f04c1 --- /dev/null +++ b/help/config/initializers/account_lifecycle.rb @@ -0,0 +1,3 @@ +ActiveSupport.on_load(:account) do + include AccountExtension::Tickets +end diff --git a/help/test/unit/account_extension_test.rb b/help/test/unit/account_extension_test.rb new file mode 100644 index 0000000..aba162c --- /dev/null +++ b/help/test/unit/account_extension_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class AccountExtensionTest < ActiveSupport::TestCase + + test "destroying an account triggers ticket destruction" do + t = FactoryGirl.create :ticket_with_creator + u = t.created_by_user + Account.new(u).destroy + assert_equal nil, Ticket.find(t.id) + end + +end diff --git a/users/app/models/account.rb b/users/app/models/account.rb index 726f642..5c943bb 100644 --- a/users/app/models/account.rb +++ b/users/app/models/account.rb @@ -1,5 +1,10 @@ # -# A Composition of a User record and it's identity records. +# The Account model takes care of the livecycle of a user. +# It composes a User record and it's identity records. +# It also allows for other engines to hook into the livecycle by +# monkeypatching the create, update and destroy methods. +# There's an ActiveSupport load_hook at the end of this file to +# make this more easy. # class Account @@ -52,4 +57,7 @@ class Account @new_identity.try(:save) && @old_identity.try(:save) end + # You can hook into the account lifecycle from different engines using + # ActiveSupport.on_load(:account) do ... + ActiveSupport.run_load_hooks(:account, self) end -- cgit v1.2.3 From ded302ebc6a9e145775f7847c5e89f91d683c777 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 6 Nov 2013 11:55:43 +0100 Subject: use the account lifecycle from UsersController#destroy --- users/app/controllers/users_controller.rb | 2 +- users/app/controllers/v1/users_controller.rb | 8 +------- users/test/functional/users_controller_test.rb | 8 ++++++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index de21983..3cbb6dc 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -47,7 +47,7 @@ class UsersController < UsersBaseController end def destroy - @user.destroy + @user.account.destroy flash[:notice] = I18n.t(:account_destroyed) # admins can destroy other users if @user != current_user diff --git a/users/app/controllers/v1/users_controller.rb b/users/app/controllers/v1/users_controller.rb index 03a5a62..0903888 100644 --- a/users/app/controllers/v1/users_controller.rb +++ b/users/app/controllers/v1/users_controller.rb @@ -24,15 +24,9 @@ module V1 end def update - account.update params[:user] + @user.account.update params[:user] respond_with @user end - protected - - def account - @user.account - end - end end diff --git a/users/test/functional/users_controller_test.rb b/users/test/functional/users_controller_test.rb index 75d900f..9c5f8d9 100644 --- a/users/test/functional/users_controller_test.rb +++ b/users/test/functional/users_controller_test.rb @@ -77,7 +77,11 @@ class UsersControllerTest < ActionController::TestCase test "admin can destroy user" do user = find_record :user + + # we destroy the user record and the associated data... user.expects(:destroy) + Identity.expects(:disable_all_for).with(user) + Ticket.expects(:destroy_all_from).with(user) login :is_admin? => true delete :destroy, :id => user.id @@ -88,7 +92,11 @@ class UsersControllerTest < ActionController::TestCase test "user can cancel account" do user = find_record :user + + # we destroy the user record and the associated data... user.expects(:destroy) + Identity.expects(:disable_all_for).with(user) + Ticket.expects(:destroy_all_from).with(user) login user expect_logout -- cgit v1.2.3 From a0e72e6ee7786e3b1fd7276f1c64912c606f4559 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 7 Nov 2013 12:46:28 +0100 Subject: only check number of disabled identities to make test more robust --- users/test/unit/identity_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/test/unit/identity_test.rb b/users/test/unit/identity_test.rb index 78ef52c..eca104f 100644 --- a/users/test/unit/identity_test.rb +++ b/users/test/unit/identity_test.rb @@ -116,7 +116,7 @@ class IdentityTest < ActiveSupport::TestCase id.save assert Identity.count > 0 Identity.destroy_all_disabled - assert_equal 0, Identity.count + assert_equal 0, Identity.disabled.count end def alias_name -- cgit v1.2.3 From e2c0962077cf759b23639276cca42606ea2135ec Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 7 Nov 2013 23:27:27 +0100 Subject: Token.destroy_all_expired to cleanup expired tokens (#4411) --- users/app/models/token.rb | 35 ++++++++++++++++++++++++----------- users/test/unit/token_test.rb | 15 +++++++++++++++ 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/users/app/models/token.rb b/users/app/models/token.rb index dd87344..bf9b0d0 100644 --- a/users/app/models/token.rb +++ b/users/app/models/token.rb @@ -11,6 +11,24 @@ class Token < CouchRest::Model::Base validates :user_id, presence: true + design do + view :by_last_seen_at + end + + def self.expires_after + APP_CONFIG[:auth] && APP_CONFIG[:auth][:token_expires_after] + end + + def self.expired + self.by_last_seen_at.endkey(expires_after.minutes.ago) + end + + def self.destroy_all_expired + self.expired.each do |token| + token.destroy + end + end + def authenticate if expired? destroy @@ -27,21 +45,16 @@ class Token < CouchRest::Model::Base end def expired? - expires_after and - last_seen_at + expires_after.minutes < Time.now - end - - def expires_after - APP_CONFIG[:auth] && APP_CONFIG[:auth][:token_expires_after] + Token.expires_after and + last_seen_at < Token.expires_after.minutes.ago end def initialize(*args) super - self.id = SecureRandom.urlsafe_base64(32).gsub(/^_*/, '') - self.last_seen_at = Time.now - end - - design do + if new_record? + self.id = SecureRandom.urlsafe_base64(32).gsub(/^_*/, '') + self.last_seen_at = Time.now + end end end diff --git a/users/test/unit/token_test.rb b/users/test/unit/token_test.rb index f56c576..445a20c 100644 --- a/users/test/unit/token_test.rb +++ b/users/test/unit/token_test.rb @@ -61,6 +61,21 @@ class ClientCertificateTest < ActiveSupport::TestCase end end + test "Token.destroy_all_expired cleans up expired tokens only" do + expired = Token.new(user_id: @user.id) + expired.last_seen_at = 2.hours.ago + expired.save + fresh = Token.new(user_id: @user.id) + fresh.save + with_config auth: {token_expires_after: 60} do + Token.destroy_all_expired + end + assert_nil Token.find(expired.id) + assert_equal fresh, Token.find(fresh.id) + fresh.destroy + end + + end -- cgit v1.2.3 From a7cd2ef0877e79302f27fb175384a0cf4ded52d9 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 7 Nov 2013 23:36:37 +0100 Subject: fix cornercase of non expiring tokens --- users/app/models/token.rb | 3 ++- users/test/factories.rb | 4 +++- users/test/unit/token_test.rb | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/users/app/models/token.rb b/users/app/models/token.rb index bf9b0d0..001eb40 100644 --- a/users/app/models/token.rb +++ b/users/app/models/token.rb @@ -20,7 +20,8 @@ class Token < CouchRest::Model::Base end def self.expired - self.by_last_seen_at.endkey(expires_after.minutes.ago) + return [] unless expires_after + by_last_seen_at.endkey(expires_after.minutes.ago) end def self.destroy_all_expired diff --git a/users/test/factories.rb b/users/test/factories.rb index c87e290..f5fb77d 100644 --- a/users/test/factories.rb +++ b/users/test/factories.rb @@ -19,6 +19,8 @@ FactoryGirl.define do end end - factory :token + factory :token do + user + end end diff --git a/users/test/unit/token_test.rb b/users/test/unit/token_test.rb index 445a20c..6c9f209 100644 --- a/users/test/unit/token_test.rb +++ b/users/test/unit/token_test.rb @@ -7,9 +7,6 @@ class ClientCertificateTest < ActiveSupport::TestCase @user = find_record :user end - teardown do - end - test "new token for user" do sample = Token.new(:user_id => @user.id) assert sample.valid? @@ -61,12 +58,17 @@ class ClientCertificateTest < ActiveSupport::TestCase end end + test "Token.destroy_all_expired is noop if no expiry is set" do + expired = FactoryGirl.create :token, last_seen_at: 2.hours.ago + with_config auth: {} do + Token.destroy_all_expired + end + assert_equal expired, Token.find(expired.id) + end + test "Token.destroy_all_expired cleans up expired tokens only" do - expired = Token.new(user_id: @user.id) - expired.last_seen_at = 2.hours.ago - expired.save - fresh = Token.new(user_id: @user.id) - fresh.save + expired = FactoryGirl.create :token, last_seen_at: 2.hours.ago + fresh = FactoryGirl.create :token with_config auth: {token_expires_after: 60} do Token.destroy_all_expired end -- cgit v1.2.3 From 69a41bee2548fa8743dd3188b0ebfc84dac17062 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 8 Nov 2013 09:48:57 +0100 Subject: removed outdated test. --- help/test/unit/ticket_test.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/help/test/unit/ticket_test.rb b/help/test/unit/ticket_test.rb index 6c29d11..751fe70 100644 --- a/help/test/unit/ticket_test.rb +++ b/help/test/unit/ticket_test.rb @@ -17,11 +17,6 @@ class TicketTest < ActiveSupport::TestCase assert !t.valid? end - test "ticket allows for multiple email addresses" do - t = FactoryGirl.build :ticket, email: 'blah@blah.com, bb@jjj.org' - assert !t.valid? - end - test "ticket open states" do t = FactoryGirl.build :ticket assert t.is_open -- cgit v1.2.3 From 8c19b447dec3982107f93ea1ae2626f844045249 Mon Sep 17 00:00:00 2001 From: Azul Date: Sun, 10 Nov 2013 21:13:31 +0100 Subject: update readme to require ruby 1.9.3 instead of 1.8 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cfdad33..1d6016c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Typically, this application is installed automatically as part of the LEAP Platf ### Install system requirements - sudo apt-get install git ruby1.8 rubygems1.8 couchdb + sudo apt-get install git ruby1.9.3 rubygems couchdb sudo gem install bundler On Debian Wheezy or later, there is a Debian package for bundler, so you can alternately run ``sudo apt-get install bundler``. -- cgit v1.2.3 From d70161b55e37e0d9e7a23ed7dbac4ea6d323971a Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 11 Nov 2013 14:16:16 -0800 Subject: Maybe not ideal fix, but since there is no edit view, we want to show the show view with the appropriate error messages. --- help/app/controllers/tickets_controller.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/help/app/controllers/tickets_controller.rb b/help/app/controllers/tickets_controller.rb index a669e19..c193ff4 100644 --- a/help/app/controllers/tickets_controller.rb +++ b/help/app/controllers/tickets_controller.rb @@ -62,14 +62,11 @@ class TicketsController < ApplicationController @ticket.comments.last.private = false unless admin? end - if @ticket.changed? - if @ticket.save - flash[:notice] = t(:changes_saved) - redirect_to_tickets - else - respond_with @ticket - end + if @ticket.changed? and @ticket.save + flash[:notice] = t(:changes_saved) + redirect_to_tickets else + flash[:error] = @ticket.errors.full_messages.join(". ") if @ticket.changed? redirect_to auto_ticket_path(@ticket) end end -- cgit v1.2.3 From 3104677b8d96cd4a118022267abc7bed818f8ddd Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 11 Nov 2013 16:02:35 -0800 Subject: Rename ticket title to be subject, as that is what we are displaying it as. --- help/app/assets/javascripts/tickets.js | 2 +- help/app/models/ticket.rb | 4 ++-- help/app/views/tickets/_edit_form.html.haml | 2 +- help/app/views/tickets/_ticket.html.haml | 2 +- help/app/views/tickets/new.html.haml | 2 +- help/test/factories.rb | 2 +- help/test/functional/tickets_controller_test.rb | 4 ++-- help/test/unit/ticket_comment_test.rb | 2 +- help/test/unit/ticket_test.rb | 6 +++--- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/help/app/assets/javascripts/tickets.js b/help/app/assets/javascripts/tickets.js index bf7965c..18537aa 100644 --- a/help/app/assets/javascripts/tickets.js +++ b/help/app/assets/javascripts/tickets.js @@ -1,4 +1,4 @@ //$(document).ready(function () { // $.fn.editable.defaults.mode = 'inline'; -// $('#title').editable(); +// $('#subject').editable(); //}); \ No newline at end of file diff --git a/help/app/models/ticket.rb b/help/app/models/ticket.rb index 74f2050..cd22758 100644 --- a/help/app/models/ticket.rb +++ b/help/app/models/ticket.rb @@ -12,7 +12,7 @@ class Ticket < CouchRest::Model::Base property :created_by, String, :protected => true # nil for anonymous tickets, should never be changed property :regarding_user, String # may be nil or valid username - property :title, String + property :subject, String property :email, String property :is_open, TrueClass, :default => true property :comments, [TicketComment] @@ -33,7 +33,7 @@ class Ticket < CouchRest::Model::Base load_views(own_path.join('..', 'designs', 'ticket')) end - validates :title, :presence => true + validates :subject, :presence => true validates :email, :allow_blank => true, :format => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ def self.search(options = {}) diff --git a/help/app/views/tickets/_edit_form.html.haml b/help/app/views/tickets/_edit_form.html.haml index 5252c2e..fa0ab44 100644 --- a/help/app/views/tickets/_edit_form.html.haml +++ b/help/app/views/tickets/_edit_form.html.haml @@ -26,7 +26,7 @@ %span.label.label-success= t(:closed) %span.label.label-clear= t(:created_by_on, :user => created_by, :time => @ticket.created_at.to_s(:short)).html_safe %div= t(:subject) - = f.text_field :title, :class => 'large full-width' + = f.text_field :subject, :class => 'large full-width' .row-fluid .span4 %div= t(:status) diff --git a/help/app/views/tickets/_ticket.html.haml b/help/app/views/tickets/_ticket.html.haml index a064c4e..5bc33c8 100644 --- a/help/app/views/tickets/_ticket.html.haml +++ b/help/app/views/tickets/_ticket.html.haml @@ -1,6 +1,6 @@ - url = auto_ticket_path(ticket) %tr - %td= link_to ticket.title, url + %td= link_to ticket.subject, url %td= link_to ticket.created_at.to_s(:short), url %td= link_to ticket.updated_at.to_s(:short), url %td= ticket.commenters diff --git a/help/app/views/tickets/new.html.haml b/help/app/views/tickets/new.html.haml index c0a343d..466d016 100644 --- a/help/app/views/tickets/new.html.haml +++ b/help/app/views/tickets/new.html.haml @@ -11,7 +11,7 @@ = simple_form_for @ticket, :validate => true, :html => {:class => 'form-horizontal'} do |f| = hidden_ticket_fields - = f.input :title, :label => t(:subject) + = f.input :subject, :label => t(:subject) - if logged_in? = f.input :email, input_html: {value: email} = f.input :regarding_user, input_html: {value: regarding} diff --git a/help/test/factories.rb b/help/test/factories.rb index bce3af1..be04f15 100644 --- a/help/test/factories.rb +++ b/help/test/factories.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :ticket do - title { Faker::Lorem.sentence } + subject { Faker::Lorem.sentence } email { Faker::Internet.email } factory :ticket_with_comment do diff --git a/help/test/functional/tickets_controller_test.rb b/help/test/functional/tickets_controller_test.rb index 3747ad0..0f56e6e 100644 --- a/help/test/functional/tickets_controller_test.rb +++ b/help/test/functional/tickets_controller_test.rb @@ -53,7 +53,7 @@ class TicketsControllerTest < ActionController::TestCase end test "should create unauthenticated ticket" do - params = {:title => "unauth ticket test title", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} + params = {:subject => "unauth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} assert_difference('Ticket.count') do post :create, :ticket => params @@ -70,7 +70,7 @@ class TicketsControllerTest < ActionController::TestCase test "should create authenticated ticket" do - params = {:title => "auth ticket test title", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} + params = {:subject => "auth ticket test subject", :comments_attributes => {"0" => {"body" =>"body of test ticket"}}} login diff --git a/help/test/unit/ticket_comment_test.rb b/help/test/unit/ticket_comment_test.rb index 44865ed..fe8cc95 100644 --- a/help/test/unit/ticket_comment_test.rb +++ b/help/test/unit/ticket_comment_test.rb @@ -36,7 +36,7 @@ class TicketCommentTest < ActiveSupport::TestCase =end test "add comments" do - testticket = Ticket.create :title => "testing" + testticket = Ticket.create :subject => "testing" assert_equal testticket.comments.count, 0 comment = TicketComment.new :body => "my email broke" #assert comment.valid? #validating or saving necessary for setting posted_at diff --git a/help/test/unit/ticket_test.rb b/help/test/unit/ticket_test.rb index 751fe70..f5e6ea7 100644 --- a/help/test/unit/ticket_test.rb +++ b/help/test/unit/ticket_test.rb @@ -44,12 +44,12 @@ class TicketTest < ActiveSupport::TestCase # TODO: do once have current_user stuff in order test "code if & only if not creator-validated" do User.current_test = nil - t1 = Ticket.create :title => 'test title' + t1 = Ticket.create :subject => 'test title' assert_not_nil t1.code assert_nil t1.created_by User.current_test = 4 - t2 = Ticket.create :title => 'test title' + t2 = Ticket.create :subject => 'test title' assert_nil t2.code assert_not_nil t2.created_by end @@ -64,7 +64,7 @@ class TicketTest < ActiveSupport::TestCase # TODO: the by_includes_post_by view is only used for tests. Maybe we should get rid of it and change the test to including ordering? - testticket = Ticket.create :title => "test retrieving commented tickets" + testticket = Ticket.create :subject => "test retrieving commented tickets" comment = TicketComment.new :body => "my email broke", :posted_by => "123" assert_equal 0, testticket.comments.count assert_equal [], Ticket.by_includes_post_by.key('123').all -- cgit v1.2.3 From 11e80906b49bea120ae398c7d6524127eaa9363a Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 12 Nov 2013 14:50:14 +0100 Subject: make sure we log json request errors and their backtraces --- app/controllers/application_controller.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b808e1c..de8d06b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,12 +10,14 @@ class ApplicationController < ActionController::Base rescue_from StandardError do |e| respond_to do |format| - format.json { render_json_error } + format.json { render_json_error(e) } format.all { raise e } # reraise the exception so the normal thing happens. end end - def render_json_error + def render_json_error(e) + Rails.logger.error e + Rails.logger.error e.backtrace.join("\n") render status: 500, json: {error: "The server failed to process your request. We'll look into it."} end -- cgit v1.2.3 From 10be8c0073b67dcfb7925996e81c2e717f8b499e Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 14 Nov 2013 02:19:03 -0800 Subject: added support for easier customizations via "config/customization" directory --- CUSTOM.md | 13 ++++++++----- config/application.rb | 6 ++++++ config/customization/README.md | 27 +++++++++++++++++++++++++++ config/initializers/customization.rb | 31 +++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 config/customization/README.md create mode 100644 config/initializers/customization.rb diff --git a/CUSTOM.md b/CUSTOM.md index 67fdac0..8671323 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -1,11 +1,14 @@ -# Customization # +Customization +============================== -Leap Web is based on Engines. All things in `app` will overwrite the default behaviour. You can either create a new rails app and include the leap_web gem or clone the leap web repository and add your customizations to the `app` directory. +Customization directory +--------------------------------------- -## CSS Customization ## +See config/customization/README.md -We use scss. It's a superset of css3. Add your customizations to `app/assets/stylesheets`. +Engines +--------------------- -## Disabling an Engine ## +Leap Web is based on Engines. All things in `app` will overwrite the default behaviour. You can either create a new rails app and include the leap_web gem or clone the leap web repository and add your customizations to the `app` directory. If you have no use for one of the engines you can remove it from the Gemfile. Not however that your app might still need to provide some functionality for the other engines to work. For example the users engine provides `current_user` and other methods. diff --git a/config/application.rb b/config/application.rb index 8587ffc..8cf7e30 100644 --- a/config/application.rb +++ b/config/application.rb @@ -85,5 +85,11 @@ module LeapWeb # Set to false in order to see asset requests in the log config.quiet_assets = true + + ## + ## CUSTOMIZATION + ## see initializers/customization.rb + ## + config.paths['app/views'].unshift "config/customization/views" end end diff --git a/config/customization/README.md b/config/customization/README.md new file mode 100644 index 0000000..9c3e434 --- /dev/null +++ b/config/customization/README.md @@ -0,0 +1,27 @@ +Customizing LEAP Webapp +============================================ + +By default, this directory is empty. Any file you place here will override the default files for the application. + +For example: + + stylesheets/ -- overrides files Rails.root/app/assets/stylesheets + tail.scss -- included before all others + head.scss -- included after all others + + public/ -- overrides files in Rails.root/public + favicon.ico -- custom favicon + img/ -- customary directory to put images in + + views/ -- overrides files Rails.root/app/views + home/ + index.html.haml -- this file is what shows up on the home page + + locales/ -- overrides files in Rails.root/config/locales + en.yml -- overrides for English + de.yml -- overrides for German + and so on... + +For most changes, the web application must be restarted after any changes are made to the customization directory. + +Sometimes a `rake tmp:clear` and a rails restart is required to pick up a new stylesheet. diff --git a/config/initializers/customization.rb b/config/initializers/customization.rb new file mode 100644 index 0000000..a2f6f88 --- /dev/null +++ b/config/initializers/customization.rb @@ -0,0 +1,31 @@ +# +# When deploying, common customizations can be dropped in config/customizations. This initializer makes this work. +# +customization_directory = "#{Rails.root}/config/customization" + +# +# Set customization views as the first view path +# +# Rails.application.config.paths['app/views'].unshift "config/customization/views" +# (For some reason, this does not work here. See application.rb for where this is actually called.) + +# +# Set customization stylesheets as the first asset path +# +# (This cannot go in application.rb, because the default paths +# haven't been loaded yet, as far as I can tell) +# +Rails.application.config.assets.paths.unshift "#{customization_directory}/stylesheets" + +# +# Copy files to public +# +if Dir.exists?("#{customization_directory}/public") + require 'fileutils' + FileUtils.cp_r("#{customization_directory}/public/.", "#{Rails.root}/public") +end + +# +# Add I18n path +# +Rails.application.config.i18n.load_path += Dir["#{customization_directory}/locales/*.{rb,yml,yaml}"] -- cgit v1.2.3 From 108938615ff7490080f80ea2d6bd1cd8037cdd84 Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 14 Nov 2013 02:19:57 -0800 Subject: minor improvements to the download button (proper localization, better image, better hooks for customization) --- core/app/helpers/download_helper.rb | 2 +- core/app/views/common/_download_for_os.html.haml | 4 ++-- core/app/views/common/_home_page_buttons.html.haml | 10 +++++++--- core/config/locales/en.yml | 4 ++-- public/leap-img/128/mask.png | Bin 10080 -> 3654 bytes 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/app/helpers/download_helper.rb b/core/app/helpers/download_helper.rb index f9c6c40..ee0fe73 100644 --- a/core/app/helpers/download_helper.rb +++ b/core/app/helpers/download_helper.rb @@ -2,7 +2,7 @@ module DownloadHelper def alternative_client_links(os = nil) alternative_clients(os).map do |client| - link_to(client.capitalize, client_download_url(client)) + link_to(I18n.t("os."+client), client_download_url(client)) end end diff --git a/core/app/views/common/_download_for_os.html.haml b/core/app/views/common/_download_for_os.html.haml index b7c88ba..4c096ce 100644 --- a/core/app/views/common/_download_for_os.html.haml +++ b/core/app/views/common/_download_for_os.html.haml @@ -8,8 +8,8 @@ %br/ %small= I18n.t("os.#{os}") %span.info - = t(:client_info, :provider => content_tag(:b,APP_CONFIG[:domain])).html_safe - %br/ + %div= t(:client_info, :provider => content_tag(:b,APP_CONFIG[:domain])).html_safe + %div - if os == "other" = t(:all_downloads_info, :clients => alternative_client_links(os).to_sentence).html_safe - else diff --git a/core/app/views/common/_home_page_buttons.html.haml b/core/app/views/common/_home_page_buttons.html.haml index e10fd38..3be12e2 100644 --- a/core/app/views/common/_home_page_buttons.html.haml +++ b/core/app/views/common/_home_page_buttons.html.haml @@ -2,10 +2,14 @@ .home-buttons .row-fluid.first - .span3 - .download.span6 + .span2 + .download.span8 = render partial: 'common/download_for_os', collection: available_clients + ['other'] - .span3 + .span2 + - if local_assigns[:divider] + .row-fluid + .span12 + = render local_assigns[:divider] .row-fluid.second .login.span4 %span.link= link_to(icon('ok-sign', icon_color) + t(:login), login_path, :class => 'btn') diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 4710a16..4abf4e8 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -23,7 +23,7 @@ en: download_client: "Download Bitmask" client_info: "The Bitmask application allows you to use %{provider} services." all_downloads_info: "It is available for %{clients}." - other_downloads_info: "It is also available for %{clients}." + other_downloads_info: "Bitmask is also available for %{clients}." login_info: "Log in to change your account settings, create support tickets, and manage payments." signup_info: "Sign up for a new user account via this website (it is better if you use the Bitmask application to sign up, but this website works too)." welcome: "Welcome to %{provider}." @@ -35,6 +35,6 @@ en: linux64: "Linux (64 bit)" windows: "Windows" android: "Android" - osx: "Mac OSX" + osx: "Mac OS" other: "(not available for your OS.)" diff --git a/public/leap-img/128/mask.png b/public/leap-img/128/mask.png index c7390eb..444a62c 100644 Binary files a/public/leap-img/128/mask.png and b/public/leap-img/128/mask.png differ -- cgit v1.2.3 From 84682ee6261967935d16fbeae1190af26420563e Mon Sep 17 00:00:00 2001 From: elijah Date: Thu, 14 Nov 2013 15:50:22 -0800 Subject: ensure that we only copy files when running restarting the app, not every time a rake task is run (especially since some rake tasks get run as root!) --- Rakefile | 2 ++ config/initializers/customization.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 8b58316..47b6c3f 100644 --- a/Rakefile +++ b/Rakefile @@ -2,6 +2,8 @@ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. +RAKE=true # let environment initialization code know if we are running via rake or not. + require 'rake/packagetask' require 'rubygems/package_task' diff --git a/config/initializers/customization.rb b/config/initializers/customization.rb index a2f6f88..08da518 100644 --- a/config/initializers/customization.rb +++ b/config/initializers/customization.rb @@ -20,7 +20,7 @@ Rails.application.config.assets.paths.unshift "#{customization_directory}/styles # # Copy files to public # -if Dir.exists?("#{customization_directory}/public") +if !defined?(RAKE) && Dir.exists?("#{customization_directory}/public") require 'fileutils' FileUtils.cp_r("#{customization_directory}/public/.", "#{Rails.root}/public") end -- cgit v1.2.3 From 4193a94b4cc5b5cabbace8311562c0ca88a79f74 Mon Sep 17 00:00:00 2001 From: elijah Date: Fri, 15 Nov 2013 00:25:40 -0800 Subject: fix problem with custom scss files and precompiling assets in production mode. --- config/application.rb | 2 +- config/initializers/customization.rb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/config/application.rb b/config/application.rb index 8cf7e30..2c9c55a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -78,7 +78,7 @@ module LeapWeb # Enable the asset pipeline config.assets.enabled = true - config.assets.initialize_on_precompile = false + config.assets.initialize_on_precompile = true # don't change this (see customization.rb) # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/config/initializers/customization.rb b/config/initializers/customization.rb index 08da518..bc9c834 100644 --- a/config/initializers/customization.rb +++ b/config/initializers/customization.rb @@ -12,8 +12,13 @@ customization_directory = "#{Rails.root}/config/customization" # # Set customization stylesheets as the first asset path # -# (This cannot go in application.rb, because the default paths -# haven't been loaded yet, as far as I can tell) +# Some notes: +# +# * This cannot go in application.rb, as far as I can tell. In application.rb, the default paths +# haven't been loaded yet, so the path we add will always end up at the end unless we add it here. +# +# * For this to work, config.assets.initialize_on_precompile MUST be set to true, otherwise +# this initializer will never get called in production mode when the assets are precompiled. # Rails.application.config.assets.paths.unshift "#{customization_directory}/stylesheets" -- cgit v1.2.3 From 6c6c3d01233cb095b3855bbdf1c33dbb90267771 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 15 Nov 2013 12:00:31 +0100 Subject: Version 0.2.7 * ensure that we only copy files for customization when restarting the app (#1300) not every time a rake task is run (especially since some rake tasks get run as root!) * improvements to the download button (proper localization, better image, better hooks for customization) * added support for easier customizations via "config/customization" directory (#1300) * log json request errors and their backtraces * show Ticket with the appropriate error messages. (*4453) * update readme to require ruby 1.9.3 instead of 1.8 * Token.destroy_all_expired to cleanup expired tokens (#4411) * use the account lifecycle from UsersController#destroy (#4216) * destroy all tickets created by a user when account is destroyed (#4216) * integration test for blocking handles after account destroyed (#4216) * disable identities to block handles after a user was deleted (#4216) * notify user their account was successfully deleted (#4216) * Fix button to enable account (#4246) --- lib/leap_web/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leap_web/version.rb b/lib/leap_web/version.rb index 0ac6e09..139f3c9 100644 --- a/lib/leap_web/version.rb +++ b/lib/leap_web/version.rb @@ -1,3 +1,3 @@ module LeapWeb - VERSION = "0.2.6" unless defined?(LeapWeb::VERSION) + VERSION = "0.2.7" unless defined?(LeapWeb::VERSION) end -- cgit v1.2.3 From eca6caac21254c72b512c0bc9b3514b544396c7c Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 18 Nov 2013 10:01:33 -0800 Subject: Remove unnecessary label. --- help/app/views/tickets/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help/app/views/tickets/new.html.haml b/help/app/views/tickets/new.html.haml index 466d016..393e5d6 100644 --- a/help/app/views/tickets/new.html.haml +++ b/help/app/views/tickets/new.html.haml @@ -11,7 +11,7 @@ = simple_form_for @ticket, :validate => true, :html => {:class => 'form-horizontal'} do |f| = hidden_ticket_fields - = f.input :subject, :label => t(:subject) + = f.input :subject - if logged_in? = f.input :email, input_html: {value: email} = f.input :regarding_user, input_html: {value: regarding} -- cgit v1.2.3 From 8e9b65b01bbd9d44d4077d94f2dc4ac375cf8e85 Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 18 Nov 2013 15:44:54 -0800 Subject: Start of service level code, which will be tweaked * stores desired & effective service level * whenever desired level is changed, effective level will be updated * allows user to set their desired service level * allow admin to update desired & effective service level --- config/defaults.yml | 23 +++++++++++++++++++++++ users/app/controllers/users_controller.rb | 6 ++++++ users/app/models/unauthenticated_user.rb | 2 ++ users/app/models/user.rb | 12 ++++++++++++ users/app/views/users/_edit.html.haml | 18 ++++++++++++++++++ 5 files changed, 61 insertions(+) diff --git a/config/defaults.yml b/config/defaults.yml index c7c8502..c986d01 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -41,12 +41,33 @@ common: &common # handles that will be allowed despite being in /etc/passwd or rfc2142 handle_whitelist: [] +service_levels: &service_levels + service_levels: + 0: + name: anonymous + cert_prefix: "LIMITED" + description: "anonymous account, with rate limited VPN" + 1: + name: free + cert_prefix: "LIMITED" + description: "free account, with rate limited VPN" + cost: 0 + quota: 100 + 2: + name: premium + cert_prefix: "UNLIMITED" + description: "premium account, with unlimited vpn" + cost: + USD: 10 + EUR: 10 + default_service_level: 1 development: <<: *downloads <<: *dev_ca <<: *cert_options <<: *common + <<: *service_levels admins: [blue, admin, admin2] domain: example.org secret_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' @@ -57,6 +78,7 @@ test: <<: *dev_ca <<: *cert_options <<: *common + <<: *service_levels admins: [admin, admin2] domain: test.me secret_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' @@ -66,6 +88,7 @@ production: <<: *downloads <<: *cert_options <<: *common + <<: *service_levels admins: [] domain: example.net payment: [] diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index 3cbb6dc..8b4715c 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -34,6 +34,12 @@ class UsersController < UsersBaseController def edit end + ## added so updating service level works, but not sure we will actually want this. also not sure that this is place to prevent user from updating own effective service level, but here as placeholder: + def update + @user.update_attributes(params[:user]) unless (!admin? and params[:user][:effective_service_level]) + respond_with @user + end + def deactivate @user.enabled = false @user.save diff --git a/users/app/models/unauthenticated_user.rb b/users/app/models/unauthenticated_user.rb index 99a6874..0fc17d2 100644 --- a/users/app/models/unauthenticated_user.rb +++ b/users/app/models/unauthenticated_user.rb @@ -1,4 +1,6 @@ # The nil object for the user class class UnauthenticatedUser < Object + # will probably want something here to return service level as APP_CONFIG[:service_levels][0] but not sure how will be accessing. + end diff --git a/users/app/models/user.rb b/users/app/models/user.rb index a14fcb5..35212a1 100644 --- a/users/app/models/user.rb +++ b/users/app/models/user.rb @@ -9,6 +9,12 @@ class User < CouchRest::Model::Base property :enabled, TrueClass, :default => true + # these will be null by default. should we set to APP_CONFIG[:default_service_level] by default, or have code assume that until these get set?: + property :desired_service_level, Integer, :accessible => true + property :effective_service_level, Integer, :accessible => true + + before_save :update_effective_service_level + validates :login, :password_salt, :password_verifier, :presence => true @@ -116,4 +122,10 @@ class User < CouchRest::Model::Base def serverside? true end + + def update_effective_service_level + if self.desired_service_level_changed? + self.effective_service_level = self.desired_service_level + end + end end diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index b86172e..d5a0ff1 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -37,6 +37,24 @@ .controls = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} + +-# TODO: probably won't want here, but here for now. Also, we will need way to ensure payment if they pick a non-free plan. +-# +-# SERVICE LEVEL +-# +- form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_service_level', :data => {token: session[:token]}}, :validate => true} += simple_form_for @user, form_options do |f| + %legend= t(:service_level) + - if @user != current_user + = t(:desired_service_level) + = f.select :desired_service_level, [[APP_CONFIG[:service_levels][1][:description], 1],[APP_CONFIG[:service_levels][2][:description], 2]], :selected => @user.desired_service_level || APP_CONFIG[:default_service_level] + - if @user != current_user + %p + = t(:effective_service_level) + = f.select :effective_service_level, [[APP_CONFIG[:service_levels][1][:description], 1],[APP_CONFIG[:service_levels][2][:description], 2]], :selected => @user.effective_service_level || APP_CONFIG[:default_service_level] + .control-group + .controls + = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} -# -# DESTROY ACCOUNT -# -- cgit v1.2.3 From f1bc68c73e7183a0ad30c6aefc6cc4cbbf1bc1f0 Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 18 Nov 2013 16:18:33 -0800 Subject: Need to cleanup some, but start to show public key for /key/username --- users/app/controllers/users_controller.rb | 6 ++++++ users/app/views/users/get_public_key.html.haml | 2 ++ users/config/routes.rb | 2 ++ 3 files changed, 10 insertions(+) create mode 100644 users/app/views/users/get_public_key.html.haml diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index 3cbb6dc..3f4daeb 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -59,4 +59,10 @@ class UsersController < UsersBaseController end end + def get_public_key + @show_navigation = false + user = User.find_by_login(params[:login]) + @public_key = user.public_key if user + end + end diff --git a/users/app/views/users/get_public_key.html.haml b/users/app/views/users/get_public_key.html.haml new file mode 100644 index 0000000..eccb367 --- /dev/null +++ b/users/app/views/users/get_public_key.html.haml @@ -0,0 +1,2 @@ +- if @public_key + = @public_key \ No newline at end of file diff --git a/users/config/routes.rb b/users/config/routes.rb index ccecfd5..1077561 100644 --- a/users/config/routes.rb +++ b/users/config/routes.rb @@ -22,4 +22,6 @@ Rails.application.routes.draw do get "/.well-known/host-meta" => 'webfinger#host_meta' get "/webfinger" => 'webfinger#search' + get "/key/:login" => 'users#get_public_key' + end -- cgit v1.2.3 From e7a8b49ae30bce36846a5ab8f1fa2bb981100224 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 19 Nov 2013 11:51:42 +0100 Subject: add dump_design_docs to CouchRest::Model::Utils:Migrate This is similar to the migrations but instead of uploading the design documents to couch it stores them in tmp/database/design.json within the rails directory. database is the supposed database name without prefixes or suffixes design is the name of the design doc CouchRest model would have created The files also contain a couchrest checksum so couchrest can detect they are up to date. This commit also cleans up a few redundant things in the extension to CouchRest::Model:Utils::Migrate that we used to have. There's no need to loop through the 'normal' models in load_all_models_with_engines since load_all_models_without_engines already does that. We were also overwriting all_models_and_proxies with exactly the same code as in the original. --- core/lib/extensions/couchrest.rb | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/core/lib/extensions/couchrest.rb b/core/lib/extensions/couchrest.rb index 91dfc1c..84cfbb3 100644 --- a/core/lib/extensions/couchrest.rb +++ b/core/lib/extensions/couchrest.rb @@ -47,28 +47,45 @@ module CouchRest def self.load_all_models_with_engines self.load_all_models_without_engines return unless defined?(Rails) - Dir[Rails.root + 'app/models/**/*.rb'].each do |path| - require path - end Dir[Rails.root + '*/app/models/**/*.rb'].each do |path| require path end end - def self.all_models_and_proxies - callbacks = migrate_each_model(find_models) - callbacks += migrate_each_proxying_model(find_proxying_models) - cleanup(callbacks) + class << self + alias_method_chain :load_all_models, :engines + end + + def dump_all_models + prepare_directory + find_models.each do |model| + model.design_docs.each do |design| + dump_design(model, design) + end + end end + protected + def dump_design(model, design) + dir = prepare_directory model.name.tableize + filename = design.id.sub('_design/','') + '.json' + puts dir + filename + design.checksum + File.open(dir + filename, "w") do |file| + file.write(JSON.pretty_generate(design.to_hash)) + end + end - class << self - alias_method_chain :load_all_models, :engines + def prepare_directory(dir = '') + dir = Rails.root + 'tmp' + 'designs' + dir + Dir.mkdir(dir) unless Dir.exists?(dir) + return dir end end end + end class ModelRailtie -- cgit v1.2.3 From cf68a79639861e69f61af85b43a3f72ed7763439 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 19 Nov 2013 15:04:14 +0100 Subject: couchrest:dump task will dump all design docs --- core/lib/tasks/leap_web_core_tasks.rake | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/lib/tasks/leap_web_core_tasks.rake b/core/lib/tasks/leap_web_core_tasks.rake index ae5b79b..734fae9 100644 --- a/core/lib/tasks/leap_web_core_tasks.rake +++ b/core/lib/tasks/leap_web_core_tasks.rake @@ -1,4 +1,9 @@ -# desc "Explaining what the task does" -# task :leap_web_core do -# # Task goes here -# end +namespace :couchrest do + + desc "Dump all the design docs found in each model" + task :dump => :environment do + CouchRest::Model::Utils::Migrate.load_all_models + CouchRest::Model::Utils::Migrate.dump_all_models + end +end + -- cgit v1.2.3 From 7596b6dbfa38d52acd447982e03e26374fb0d747 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 21 Nov 2013 12:49:25 +0100 Subject: rake tasks clean up expired tokens and sessions (#4568) --- Gemfile.lock | 4 ++-- core/leap_web_core.gemspec | 2 +- core/lib/tasks/leap_web_core_tasks.rake | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e6096fd..8d80546 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ PATH leap_web_core (0.2.5) couchrest (~> 1.1.3) couchrest_model (~> 2.0.0) - couchrest_session_store (~> 0.2.0) + couchrest_session_store (~> 0.2.1) json rails (~> 3.2.11) @@ -110,7 +110,7 @@ GEM couchrest (~> 1.1.3) mime-types (>= 1.15) tzinfo (>= 0.3.22) - couchrest_session_store (0.2.0) + couchrest_session_store (0.2.1) actionpack couchrest couchrest_model diff --git a/core/leap_web_core.gemspec b/core/leap_web_core.gemspec index e98c892..c745f00 100644 --- a/core/leap_web_core.gemspec +++ b/core/leap_web_core.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.add_dependency "couchrest", "~> 1.1.3" s.add_dependency "couchrest_model", "~> 2.0.0" - s.add_dependency "couchrest_session_store", "~> 0.2.0" + s.add_dependency "couchrest_session_store", "~> 0.2.1" s.add_dependency "json" end diff --git a/core/lib/tasks/leap_web_core_tasks.rake b/core/lib/tasks/leap_web_core_tasks.rake index 734fae9..ec6abac 100644 --- a/core/lib/tasks/leap_web_core_tasks.rake +++ b/core/lib/tasks/leap_web_core_tasks.rake @@ -7,3 +7,19 @@ namespace :couchrest do end end +namespace :cleanup do + + desc "Cleanup all expired session documents" + task :sessions => :environment do + # make sure this is the same as in + # config/initializers/session_store.rb + store = CouchRest::Session::Store.new expire_after: 1800 + store.cleanup(store.expired) + end + + desc "Cleanup all expired tokens" + task :tokens => :environment do + Token.destroy_all_expired + end +end + -- cgit v1.2.3 From d82ea5da2aa705bcfa74f2a8b42a197883b694e3 Mon Sep 17 00:00:00 2001 From: jessib Date: Thu, 21 Nov 2013 12:15:03 -0800 Subject: Refactoring of code, and tests. --- users/app/controllers/keys_controller.rb | 10 +++++++++ users/app/controllers/users_controller.rb | 6 ----- users/app/views/users/get_public_key.html.haml | 2 -- users/config/routes.rb | 2 +- users/test/functional/keys_controller_test.rb | 31 ++++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 users/app/controllers/keys_controller.rb delete mode 100644 users/app/views/users/get_public_key.html.haml create mode 100644 users/test/functional/keys_controller_test.rb diff --git a/users/app/controllers/keys_controller.rb b/users/app/controllers/keys_controller.rb new file mode 100644 index 0000000..9a39fc4 --- /dev/null +++ b/users/app/controllers/keys_controller.rb @@ -0,0 +1,10 @@ +class KeysController < ApplicationController + + def show + user = User.find_by_login(params[:login]) + # layout won't be included if we render text + # we will show blank page if user doesn't have key or user doesn't exist + render text: user ? user.public_key : '' + end + +end diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index 3f4daeb..3cbb6dc 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -59,10 +59,4 @@ class UsersController < UsersBaseController end end - def get_public_key - @show_navigation = false - user = User.find_by_login(params[:login]) - @public_key = user.public_key if user - end - end diff --git a/users/app/views/users/get_public_key.html.haml b/users/app/views/users/get_public_key.html.haml deleted file mode 100644 index eccb367..0000000 --- a/users/app/views/users/get_public_key.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -- if @public_key - = @public_key \ No newline at end of file diff --git a/users/config/routes.rb b/users/config/routes.rb index 1077561..69f9cf7 100644 --- a/users/config/routes.rb +++ b/users/config/routes.rb @@ -22,6 +22,6 @@ Rails.application.routes.draw do get "/.well-known/host-meta" => 'webfinger#host_meta' get "/webfinger" => 'webfinger#search' - get "/key/:login" => 'users#get_public_key' + get "/key/:login" => 'keys#show' end diff --git a/users/test/functional/keys_controller_test.rb b/users/test/functional/keys_controller_test.rb new file mode 100644 index 0000000..9cc88d1 --- /dev/null +++ b/users/test/functional/keys_controller_test.rb @@ -0,0 +1,31 @@ +require 'test_helper' + +class KeysControllerTest < ActionController::TestCase + + test "get existing public key" do + public_key = 'my public key' + @user = stub_record :user, :public_key => public_key + User.stubs(:find_by_login).with(@user.login).returns(@user) + get :show, :login => @user.login + assert_response :success + assert_equal "text/html", response.content_type + assert_equal public_key, response.body + end + + test "get non-existing public key for user" do + @user = stub_record :user + User.stubs(:find_by_login).with(@user.login).returns(@user) + get :show, :login => @user.login + assert_response :success + assert_equal "text/html", response.content_type + assert_equal '', response.body.strip + end + + test "get public key for non-existing user" do + get :show, :login => 'asdkljslksjfdlskfj' + assert_response :success + assert_equal "text/html", response.content_type + assert_equal '', response.body.strip + end + +end -- cgit v1.2.3 From 299dfdf4164ee10de63aa2543935eeed65437b3f Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 25 Nov 2013 11:31:33 -0800 Subject: Give 404 error if one goes to /key/user for non-existing user. --- users/app/controllers/keys_controller.rb | 6 ++++-- users/test/functional/keys_controller_test.rb | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/users/app/controllers/keys_controller.rb b/users/app/controllers/keys_controller.rb index 9a39fc4..949f2c0 100644 --- a/users/app/controllers/keys_controller.rb +++ b/users/app/controllers/keys_controller.rb @@ -3,8 +3,10 @@ class KeysController < ApplicationController def show user = User.find_by_login(params[:login]) # layout won't be included if we render text - # we will show blank page if user doesn't have key or user doesn't exist - render text: user ? user.public_key : '' + # we will show blank page if user doesn't have key (which shouldn't generally occur) + # and a 404 error if user doesn't exist + user ? (render text: user.public_key) : (raise ActionController::RoutingError.new('Not Found')) + end end diff --git a/users/test/functional/keys_controller_test.rb b/users/test/functional/keys_controller_test.rb index 9cc88d1..b69cbc0 100644 --- a/users/test/functional/keys_controller_test.rb +++ b/users/test/functional/keys_controller_test.rb @@ -13,6 +13,7 @@ class KeysControllerTest < ActionController::TestCase end test "get non-existing public key for user" do + # this isn't a scenerio that should generally occur. @user = stub_record :user User.stubs(:find_by_login).with(@user.login).returns(@user) get :show, :login => @user.login @@ -22,10 +23,10 @@ class KeysControllerTest < ActionController::TestCase end test "get public key for non-existing user" do - get :show, :login => 'asdkljslksjfdlskfj' - assert_response :success - assert_equal "text/html", response.content_type - assert_equal '', response.body.strip + # raise 404 error if user doesn't exist (doesn't need to be this routing error, but seems fine to assume for now): + assert_raise(ActionController::RoutingError) { + get :show, :login => 'asdkljslksjfdlskfj' + } end end -- cgit v1.2.3 From 7de12c71ce7eb4eeb6e0795275434ed4a4120c25 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 26 Nov 2013 11:22:47 +0100 Subject: ignore attempts to empty public_key, refactor refactor: prepare validations of the uploaded pgp keys --- users/app/models/account.rb | 11 ++++++++--- users/app/models/identity.rb | 6 +++--- users/app/models/pgp_key.rb | 25 +++++++++++++++++++++++++ users/test/integration/api/account_flow_test.rb | 6 +++--- 4 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 users/app/models/pgp_key.rb diff --git a/users/app/models/account.rb b/users/app/models/account.rb index 5c943bb..cf998e4 100644 --- a/users/app/models/account.rb +++ b/users/app/models/account.rb @@ -27,7 +27,8 @@ class Account @user.update_attributes attrs.slice(:password_verifier, :password_salt) end # TODO: move into identity controller - update_pgp_key(attrs[:public_key]) if attrs.has_key? :public_key + key = update_pgp_key(attrs[:public_key]) + @user.errors.set :public_key, key.errors.full_messages @user.save && save_identities @user.refresh_identity end @@ -49,8 +50,12 @@ class Account end def update_pgp_key(key) - @new_identity ||= Identity.for(@user) - @new_identity.set_key(:pgp, key) + PgpKey.new(key).tap do |key| + if key.present? && key.valid? + @new_identity ||= Identity.for(@user) + @new_identity.set_key(:pgp, key) + end + end end def save_identities diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb index 97966d0..cbb540e 100644 --- a/users/app/models/identity.rb +++ b/users/app/models/identity.rb @@ -94,9 +94,9 @@ class Identity < CouchRest::Model::Base read_attribute('keys') || HashWithIndifferentAccess.new end - def set_key(type, value) - return if keys[type] == value - write_attribute('keys', keys.merge(type => value)) + def set_key(type, key) + return if keys[type] == key.to_s + write_attribute('keys', keys.merge(type => key.to_s)) end # for LoginFormatValidation diff --git a/users/app/models/pgp_key.rb b/users/app/models/pgp_key.rb new file mode 100644 index 0000000..fddec1e --- /dev/null +++ b/users/app/models/pgp_key.rb @@ -0,0 +1,25 @@ +class PgpKey + include ActiveModel::Validations + + # mostly for testing. + attr_accessor :key_block + + def initialize(key_block = nil) + @key_block = key_block + end + + def to_s + @key_block + end + + def present? + @key_block.present? + end + + # let's allow comparison with plain key_block strings. + def ==(other) + self.equal?(other) or + self.to_s == other + end + +end diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb index e41befa..90f2a97 100644 --- a/users/test/integration/api/account_flow_test.rb +++ b/users/test/integration/api/account_flow_test.rb @@ -114,9 +114,9 @@ class AccountFlowTest < RackTest # should not overwrite public key: put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:blee => :blah}, :format => :json assert_equal test_public_key, Identity.for(@user).keys[:pgp] - # should overwrite public key: - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => nil}, :format => :json - assert_nil Identity.for(@user).keys[:pgp] + # should not empty public key: + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => ""}, :format => :json + assert_equal test_public_key, Identity.for(@user).keys[:pgp] end end -- cgit v1.2.3 From e34141c3265c6daeda92bcb83fa508de00551bc3 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 26 Nov 2013 14:39:42 +0100 Subject: simple validation for pgp key format --- users/app/models/pgp_key.rb | 37 ++++++++++++++++++++----- users/test/factories.rb | 8 ++++++ users/test/integration/api/account_flow_test.rb | 34 ++++++++++++++++------- users/test/integration/browser/account_test.rb | 4 +-- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/users/app/models/pgp_key.rb b/users/app/models/pgp_key.rb index fddec1e..66f8660 100644 --- a/users/app/models/pgp_key.rb +++ b/users/app/models/pgp_key.rb @@ -1,25 +1,48 @@ class PgpKey include ActiveModel::Validations + KEYBLOCK_IDENTIFIERS = [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + '-----END PGP PUBLIC KEY BLOCK-----', + ] + # mostly for testing. - attr_accessor :key_block + attr_accessor :keyblock + + validate :validate_keyblock_format - def initialize(key_block = nil) - @key_block = key_block + def initialize(keyblock = nil) + @keyblock = keyblock end def to_s - @key_block + @keyblock end def present? - @key_block.present? + @keyblock.present? end - # let's allow comparison with plain key_block strings. + # allow comparison with plain keyblock strings. def ==(other) self.equal?(other) or - self.to_s == other + # relax the comparison on line ends. + self.to_s.tr_s("\n\r", '') == other.tr_s("\r\n", '') + end + + protected + + def validate_keyblock_format + if keyblock_identifier_missing? + errors.add :public_key_block, + "does not look like an armored pgp public key block" + end + end + + def keyblock_identifier_missing? + KEYBLOCK_IDENTIFIERS.find do |identify| + !@keyblock.include?(identify) + end end end diff --git a/users/test/factories.rb b/users/test/factories.rb index f5fb77d..ae00d43 100644 --- a/users/test/factories.rb +++ b/users/test/factories.rb @@ -23,4 +23,12 @@ FactoryGirl.define do user end + factory :pgp_key do + keyblock <<-EOPGP +-----BEGIN PGP PUBLIC KEY BLOCK----- ++Dummy+PGP+KEY+++Dummy+PGP+KEY+++Dummy+PGP+KEY+++Dummy+PGP+KEY+ +#{SecureRandom.base64(4032)} +-----END PGP PUBLIC KEY BLOCK----- + EOPGP + end end diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb index 90f2a97..9aee38b 100644 --- a/users/test/integration/api/account_flow_test.rb +++ b/users/test/integration/api/account_flow_test.rb @@ -96,27 +96,41 @@ class AccountFlowTest < RackTest assert server_auth["M2"] end - test "update user" do + test "changing login" do server_auth = @srp.authenticate(self) - test_public_key = 'asdlfkjslfdkjasd' original_login = @user.login new_login = 'zaph' User.find_by_login(new_login).try(:destroy) Identity.by_address.key(new_login + '@' + APP_CONFIG[:domain]).each do |identity| identity.destroy end - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => test_public_key, :login => new_login}, :format => :json + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:login => new_login}, :format => :json assert last_response.successful? - assert_equal test_public_key, Identity.for(@user).keys[:pgp] # does not change login if no password_verifier is present assert_equal original_login, @user.login - # eventually probably want to remove most of this into a non-integration functional test - # should not overwrite public key: - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:blee => :blah}, :format => :json - assert_equal test_public_key, Identity.for(@user).keys[:pgp] - # should not empty public key: + end + + test "upload pgp key" do + server_auth = @srp.authenticate(self) + key = FactoryGirl.build :pgp_key + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => key}, :format => :json + assert_equal key, Identity.for(@user).keys[:pgp] + end + + # eventually probably want to remove most of this into a non-integration + # functional test + test "prevent uploading invalid key" do + server_auth = @srp.authenticate(self) + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => :blah}, :format => :json + assert_nil Identity.for(@user).keys[:pgp] + end + + test "prevent emptying public key" do + server_auth = @srp.authenticate(self) + key = FactoryGirl.build :pgp_key + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => key}, :format => :json put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => ""}, :format => :json - assert_equal test_public_key, Identity.for(@user).keys[:pgp] + assert_equal key, Identity.for(@user).keys[:pgp] end end diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index b349489..3d281ae 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -66,7 +66,7 @@ class AccountTest < BrowserIntegrationTest end test "change pgp key" do - pgp_key = "My PGP Key Stub" + pgp_key = FactoryGirl.build :pgp_key username, password = submit_signup click_on "Account Settings" within('#update_pgp_key') do @@ -76,7 +76,7 @@ class AccountTest < BrowserIntegrationTest page.assert_selector 'input[value="Saving..."]' # at some point we're done: page.assert_no_selector 'input[value="Saving..."]' - assert page.has_field? 'Public key', with: pgp_key + assert page.has_field? 'Public key', with: pgp_key.to_s user = User.find_by_login(username) assert_equal pgp_key, user.public_key user.account.destroy -- cgit v1.2.3 From dade6497424a869db5f1dfb030f88f4711278b81 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 27 Nov 2013 09:43:28 +0100 Subject: minor: rename test to what it actually tests [skip ci] --- users/test/integration/api/account_flow_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb index 9aee38b..edd0859 100644 --- a/users/test/integration/api/account_flow_test.rb +++ b/users/test/integration/api/account_flow_test.rb @@ -96,7 +96,7 @@ class AccountFlowTest < RackTest assert server_auth["M2"] end - test "changing login" do + test "prevent changing login without changing password_verifier" do server_auth = @srp.authenticate(self) original_login = @user.login new_login = 'zaph' -- cgit v1.2.3 From b75f8781bb55557f13a9a4ae48fc45e5d6f1ee86 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 2 Dec 2013 15:23:40 +0100 Subject: Version 0.2.8 - release candidate * Return public key on /key/:login * rake tasks clean up expired tokens and sessions (#4568) * rake task to dump design docs to files * add dump_design_docs to CouchRest::Model::Utils:Migrate * rename ticket title to subject --- lib/leap_web/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leap_web/version.rb b/lib/leap_web/version.rb index 139f3c9..f6f99ae 100644 --- a/lib/leap_web/version.rb +++ b/lib/leap_web/version.rb @@ -1,3 +1,3 @@ module LeapWeb - VERSION = "0.2.7" unless defined?(LeapWeb::VERSION) + VERSION = "0.2.8.rc" unless defined?(LeapWeb::VERSION) end -- cgit v1.2.3 From 35761333404e3cc2c93bca23036d0fd8e47fd10b Mon Sep 17 00:00:00 2001 From: jessib Date: Tue, 3 Dec 2013 12:17:51 -0800 Subject: Add ServiceLevel class to wrap config and give accessors. Has some hacky parts, but seems like okay generic start for now. --- users/app/models/service_level.rb | 31 +++++++++++++++++++++++++++++++ users/app/models/user.rb | 22 +++++++++++++++++----- users/app/views/users/_edit.html.haml | 6 ++++-- 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 users/app/models/service_level.rb diff --git a/users/app/models/service_level.rb b/users/app/models/service_level.rb new file mode 100644 index 0000000..ac5244f --- /dev/null +++ b/users/app/models/service_level.rb @@ -0,0 +1,31 @@ +class ServiceLevel + + def initialize(attributes = {}) + @level = attributes[:level] || APP_CONFIG[:default_service_level] + end + + def level + @level + end + + def name + APP_CONFIG[:service_levels][@level][:name] + end + + def cert_prefix + APP_CONFIG[:service_levels][@level][:cert_prefix] + end + + def description + APP_CONFIG[:service_levels][@level][:description] + end + + def cost + APP_CONFIG[:service_levels][@level][:cost] + end + + def quota + APP_CONFIG[:service_levels][@level][:quota] + end + +end diff --git a/users/app/models/user.rb b/users/app/models/user.rb index 35212a1..621ff4e 100644 --- a/users/app/models/user.rb +++ b/users/app/models/user.rb @@ -9,9 +9,9 @@ class User < CouchRest::Model::Base property :enabled, TrueClass, :default => true - # these will be null by default. should we set to APP_CONFIG[:default_service_level] by default, or have code assume that until these get set?: - property :desired_service_level, Integer, :accessible => true - property :effective_service_level, Integer, :accessible => true + # these will be null by default but we shouldn't ever pull them directly, but only via the methods that will return the full ServiceLevel + property :desired_service_level_code, Integer, :accessible => true + property :effective_service_level_code, Integer, :accessible => true before_save :update_effective_service_level @@ -100,6 +100,16 @@ class User < CouchRest::Model::Base @identity = Identity.for(self) end + def desired_service_level + code = self.desired_service_level_code || APP_CONFIG[:default_service_level] + ServiceLevel.new({level: code}) + end + + def effective_service_level + code = self.effective_service_level_code || self.desired_service_level.level + ServiceLevel.new({level: code}) + end + protected ## @@ -124,8 +134,10 @@ class User < CouchRest::Model::Base end def update_effective_service_level - if self.desired_service_level_changed? - self.effective_service_level = self.desired_service_level + # TODO: Is this always the case? Might there be a situation where the admin has set the effective service level and we don't want it changed to match the desired one? + if self.desired_service_level_code_changed? + self.effective_service_level_code = self.desired_service_level_code end end + end diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index d5a0ff1..d2c2d95 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -47,11 +47,13 @@ %legend= t(:service_level) - if @user != current_user = t(:desired_service_level) - = f.select :desired_service_level, [[APP_CONFIG[:service_levels][1][:description], 1],[APP_CONFIG[:service_levels][2][:description], 2]], :selected => @user.desired_service_level || APP_CONFIG[:default_service_level] + - sl1 = ServiceLevel.new({level: 1}) #ugly but okay for now + - sl2 = ServiceLevel.new({level: 2}) + = f.select :desired_service_level_code, [[sl1.description, sl1.level],[sl2.description, sl2.level]], :selected => @user.desired_service_level.level - if @user != current_user %p = t(:effective_service_level) - = f.select :effective_service_level, [[APP_CONFIG[:service_levels][1][:description], 1],[APP_CONFIG[:service_levels][2][:description], 2]], :selected => @user.effective_service_level || APP_CONFIG[:default_service_level] + = f.select :effective_service_level_code, [[sl1.description, sl1.level],[sl2.description, sl2.level]], :selected => @user.effective_service_level.level .control-group .controls = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} -- cgit v1.2.3 From 7d4a9658c29cad526cfe5c952f71109e8eb304e7 Mon Sep 17 00:00:00 2001 From: jessib Date: Tue, 3 Dec 2013 15:07:11 -0800 Subject: Some simplification of code. --- users/app/models/service_level.rb | 26 +++++++------------------- users/app/models/user.rb | 6 +++--- users/app/views/users/_edit.html.haml | 6 ++---- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/users/app/models/service_level.rb b/users/app/models/service_level.rb index ac5244f..299aaf1 100644 --- a/users/app/models/service_level.rb +++ b/users/app/models/service_level.rb @@ -1,31 +1,19 @@ class ServiceLevel def initialize(attributes = {}) - @level = attributes[:level] || APP_CONFIG[:default_service_level] + @id = attributes[:id] || APP_CONFIG[:default_service_level] end - def level - @level + def self.authenticated_select_options + APP_CONFIG[:service_levels].map { |id,config_hash| [config_hash[:description], id] if config_hash[:name] != 'anonymous'}.compact end - def name - APP_CONFIG[:service_levels][@level][:name] + def id + @id end - def cert_prefix - APP_CONFIG[:service_levels][@level][:cert_prefix] - end - - def description - APP_CONFIG[:service_levels][@level][:description] - end - - def cost - APP_CONFIG[:service_levels][@level][:cost] - end - - def quota - APP_CONFIG[:service_levels][@level][:quota] + def config_hash + APP_CONFIG[:service_levels][@id] end end diff --git a/users/app/models/user.rb b/users/app/models/user.rb index 621ff4e..720f5a9 100644 --- a/users/app/models/user.rb +++ b/users/app/models/user.rb @@ -102,12 +102,12 @@ class User < CouchRest::Model::Base def desired_service_level code = self.desired_service_level_code || APP_CONFIG[:default_service_level] - ServiceLevel.new({level: code}) + ServiceLevel.new({id: code}) end def effective_service_level - code = self.effective_service_level_code || self.desired_service_level.level - ServiceLevel.new({level: code}) + code = self.effective_service_level_code || self.desired_service_level.id + ServiceLevel.new({id: code}) end protected diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index d2c2d95..897c54b 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -47,13 +47,11 @@ %legend= t(:service_level) - if @user != current_user = t(:desired_service_level) - - sl1 = ServiceLevel.new({level: 1}) #ugly but okay for now - - sl2 = ServiceLevel.new({level: 2}) - = f.select :desired_service_level_code, [[sl1.description, sl1.level],[sl2.description, sl2.level]], :selected => @user.desired_service_level.level + = f.select :desired_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.desired_service_level.id - if @user != current_user %p = t(:effective_service_level) - = f.select :effective_service_level_code, [[sl1.description, sl1.level],[sl2.description, sl2.level]], :selected => @user.effective_service_level.level + = f.select :effective_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.effective_service_level.id .control-group .controls = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} -- cgit v1.2.3 From 6ebf095bc553345a3e0b8c48cadc3e1440c59ca5 Mon Sep 17 00:00:00 2001 From: jessib Date: Thu, 5 Dec 2013 11:11:39 -0800 Subject: We won't want service levels in production mode, and have it so this initial service level code won't break anything if it isn't set in the config. --- config/defaults.yml | 1 - users/app/views/users/_edit.html.haml | 27 ++++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/config/defaults.yml b/config/defaults.yml index c986d01..4530d47 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -88,7 +88,6 @@ production: <<: *downloads <<: *cert_options <<: *common - <<: *service_levels admins: [] domain: example.net payment: [] diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index 897c54b..0b36d6e 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -42,19 +42,20 @@ -# -# SERVICE LEVEL -# -- form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_service_level', :data => {token: session[:token]}}, :validate => true} -= simple_form_for @user, form_options do |f| - %legend= t(:service_level) - - if @user != current_user - = t(:desired_service_level) - = f.select :desired_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.desired_service_level.id - - if @user != current_user - %p - = t(:effective_service_level) - = f.select :effective_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.effective_service_level.id - .control-group - .controls - = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} +- if APP_CONFIG[:service_levels] + - form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_service_level', :data => {token: session[:token]}}, :validate => true} + = simple_form_for @user, form_options do |f| + %legend= t(:service_level) + - if @user != current_user + = t(:desired_service_level) + = f.select :desired_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.desired_service_level.id + - if @user != current_user + %p + = t(:effective_service_level) + = f.select :effective_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.effective_service_level.id + .control-group + .controls + = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} -# -# DESTROY ACCOUNT -# -- cgit v1.2.3 From 015ad90c98c55a078fdfe723d470ad732e807737 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 6 Dec 2013 15:04:54 +0100 Subject: Version 0.2.8 * Return public key on /key/:login * rake tasks clean up expired tokens and sessions (#4568) * rake task to dump design docs to files * add dump_design_docs to CouchRest::Model::Utils:Migrate * rename ticket title to subject --- lib/leap_web/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/leap_web/version.rb b/lib/leap_web/version.rb index f6f99ae..983e3ad 100644 --- a/lib/leap_web/version.rb +++ b/lib/leap_web/version.rb @@ -1,3 +1,3 @@ module LeapWeb - VERSION = "0.2.8.rc" unless defined?(LeapWeb::VERSION) + VERSION = "0.2.8" unless defined?(LeapWeb::VERSION) end -- cgit v1.2.3 From e64e746759bb241536612c949361442f269ef2f0 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 26 Nov 2013 11:22:47 +0100 Subject: ignore attempts to empty public_key, refactor refactor: prepare validations of the uploaded pgp keys --- users/app/models/account.rb | 11 ++++++++--- users/app/models/identity.rb | 6 +++--- users/app/models/pgp_key.rb | 25 +++++++++++++++++++++++++ users/test/integration/api/account_flow_test.rb | 6 +++--- 4 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 users/app/models/pgp_key.rb diff --git a/users/app/models/account.rb b/users/app/models/account.rb index 5c943bb..cf998e4 100644 --- a/users/app/models/account.rb +++ b/users/app/models/account.rb @@ -27,7 +27,8 @@ class Account @user.update_attributes attrs.slice(:password_verifier, :password_salt) end # TODO: move into identity controller - update_pgp_key(attrs[:public_key]) if attrs.has_key? :public_key + key = update_pgp_key(attrs[:public_key]) + @user.errors.set :public_key, key.errors.full_messages @user.save && save_identities @user.refresh_identity end @@ -49,8 +50,12 @@ class Account end def update_pgp_key(key) - @new_identity ||= Identity.for(@user) - @new_identity.set_key(:pgp, key) + PgpKey.new(key).tap do |key| + if key.present? && key.valid? + @new_identity ||= Identity.for(@user) + @new_identity.set_key(:pgp, key) + end + end end def save_identities diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb index 97966d0..cbb540e 100644 --- a/users/app/models/identity.rb +++ b/users/app/models/identity.rb @@ -94,9 +94,9 @@ class Identity < CouchRest::Model::Base read_attribute('keys') || HashWithIndifferentAccess.new end - def set_key(type, value) - return if keys[type] == value - write_attribute('keys', keys.merge(type => value)) + def set_key(type, key) + return if keys[type] == key.to_s + write_attribute('keys', keys.merge(type => key.to_s)) end # for LoginFormatValidation diff --git a/users/app/models/pgp_key.rb b/users/app/models/pgp_key.rb new file mode 100644 index 0000000..fddec1e --- /dev/null +++ b/users/app/models/pgp_key.rb @@ -0,0 +1,25 @@ +class PgpKey + include ActiveModel::Validations + + # mostly for testing. + attr_accessor :key_block + + def initialize(key_block = nil) + @key_block = key_block + end + + def to_s + @key_block + end + + def present? + @key_block.present? + end + + # let's allow comparison with plain key_block strings. + def ==(other) + self.equal?(other) or + self.to_s == other + end + +end diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb index e41befa..90f2a97 100644 --- a/users/test/integration/api/account_flow_test.rb +++ b/users/test/integration/api/account_flow_test.rb @@ -114,9 +114,9 @@ class AccountFlowTest < RackTest # should not overwrite public key: put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:blee => :blah}, :format => :json assert_equal test_public_key, Identity.for(@user).keys[:pgp] - # should overwrite public key: - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => nil}, :format => :json - assert_nil Identity.for(@user).keys[:pgp] + # should not empty public key: + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => ""}, :format => :json + assert_equal test_public_key, Identity.for(@user).keys[:pgp] end end -- cgit v1.2.3 From 242f55a55cc51ebc21dd027966cbdf598fcd071d Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 26 Nov 2013 14:39:42 +0100 Subject: simple validation for pgp key format --- users/app/models/pgp_key.rb | 37 ++++++++++++++++++++----- users/test/factories.rb | 8 ++++++ users/test/integration/api/account_flow_test.rb | 34 ++++++++++++++++------- users/test/integration/browser/account_test.rb | 4 +-- 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/users/app/models/pgp_key.rb b/users/app/models/pgp_key.rb index fddec1e..66f8660 100644 --- a/users/app/models/pgp_key.rb +++ b/users/app/models/pgp_key.rb @@ -1,25 +1,48 @@ class PgpKey include ActiveModel::Validations + KEYBLOCK_IDENTIFIERS = [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + '-----END PGP PUBLIC KEY BLOCK-----', + ] + # mostly for testing. - attr_accessor :key_block + attr_accessor :keyblock + + validate :validate_keyblock_format - def initialize(key_block = nil) - @key_block = key_block + def initialize(keyblock = nil) + @keyblock = keyblock end def to_s - @key_block + @keyblock end def present? - @key_block.present? + @keyblock.present? end - # let's allow comparison with plain key_block strings. + # allow comparison with plain keyblock strings. def ==(other) self.equal?(other) or - self.to_s == other + # relax the comparison on line ends. + self.to_s.tr_s("\n\r", '') == other.tr_s("\r\n", '') + end + + protected + + def validate_keyblock_format + if keyblock_identifier_missing? + errors.add :public_key_block, + "does not look like an armored pgp public key block" + end + end + + def keyblock_identifier_missing? + KEYBLOCK_IDENTIFIERS.find do |identify| + !@keyblock.include?(identify) + end end end diff --git a/users/test/factories.rb b/users/test/factories.rb index f5fb77d..ae00d43 100644 --- a/users/test/factories.rb +++ b/users/test/factories.rb @@ -23,4 +23,12 @@ FactoryGirl.define do user end + factory :pgp_key do + keyblock <<-EOPGP +-----BEGIN PGP PUBLIC KEY BLOCK----- ++Dummy+PGP+KEY+++Dummy+PGP+KEY+++Dummy+PGP+KEY+++Dummy+PGP+KEY+ +#{SecureRandom.base64(4032)} +-----END PGP PUBLIC KEY BLOCK----- + EOPGP + end end diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb index 90f2a97..9aee38b 100644 --- a/users/test/integration/api/account_flow_test.rb +++ b/users/test/integration/api/account_flow_test.rb @@ -96,27 +96,41 @@ class AccountFlowTest < RackTest assert server_auth["M2"] end - test "update user" do + test "changing login" do server_auth = @srp.authenticate(self) - test_public_key = 'asdlfkjslfdkjasd' original_login = @user.login new_login = 'zaph' User.find_by_login(new_login).try(:destroy) Identity.by_address.key(new_login + '@' + APP_CONFIG[:domain]).each do |identity| identity.destroy end - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => test_public_key, :login => new_login}, :format => :json + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:login => new_login}, :format => :json assert last_response.successful? - assert_equal test_public_key, Identity.for(@user).keys[:pgp] # does not change login if no password_verifier is present assert_equal original_login, @user.login - # eventually probably want to remove most of this into a non-integration functional test - # should not overwrite public key: - put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:blee => :blah}, :format => :json - assert_equal test_public_key, Identity.for(@user).keys[:pgp] - # should not empty public key: + end + + test "upload pgp key" do + server_auth = @srp.authenticate(self) + key = FactoryGirl.build :pgp_key + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => key}, :format => :json + assert_equal key, Identity.for(@user).keys[:pgp] + end + + # eventually probably want to remove most of this into a non-integration + # functional test + test "prevent uploading invalid key" do + server_auth = @srp.authenticate(self) + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => :blah}, :format => :json + assert_nil Identity.for(@user).keys[:pgp] + end + + test "prevent emptying public key" do + server_auth = @srp.authenticate(self) + key = FactoryGirl.build :pgp_key + put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => key}, :format => :json put "http://api.lvh.me:3000/1/users/" + @user.id + '.json', :user => {:public_key => ""}, :format => :json - assert_equal test_public_key, Identity.for(@user).keys[:pgp] + assert_equal key, Identity.for(@user).keys[:pgp] end end diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index b349489..3d281ae 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -66,7 +66,7 @@ class AccountTest < BrowserIntegrationTest end test "change pgp key" do - pgp_key = "My PGP Key Stub" + pgp_key = FactoryGirl.build :pgp_key username, password = submit_signup click_on "Account Settings" within('#update_pgp_key') do @@ -76,7 +76,7 @@ class AccountTest < BrowserIntegrationTest page.assert_selector 'input[value="Saving..."]' # at some point we're done: page.assert_no_selector 'input[value="Saving..."]' - assert page.has_field? 'Public key', with: pgp_key + assert page.has_field? 'Public key', with: pgp_key.to_s user = User.find_by_login(username) assert_equal pgp_key, user.public_key user.account.destroy -- cgit v1.2.3 From 7fa78df26fccea56ef2911c93a26f41f8eae3fdf Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 27 Nov 2013 09:43:28 +0100 Subject: minor: rename test to what it actually tests [skip ci] --- users/test/integration/api/account_flow_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/test/integration/api/account_flow_test.rb b/users/test/integration/api/account_flow_test.rb index 9aee38b..edd0859 100644 --- a/users/test/integration/api/account_flow_test.rb +++ b/users/test/integration/api/account_flow_test.rb @@ -96,7 +96,7 @@ class AccountFlowTest < RackTest assert server_auth["M2"] end - test "changing login" do + test "prevent changing login without changing password_verifier" do server_auth = @srp.authenticate(self) original_login = @user.login new_login = 'zaph' -- cgit v1.2.3 From a013b03b0b715ec1209d2812da52ff5f0831c833 Mon Sep 17 00:00:00 2001 From: elijah Date: Fri, 6 Dec 2013 15:45:02 -0800 Subject: make sure key responses are plain text --- users/app/controllers/keys_controller.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/users/app/controllers/keys_controller.rb b/users/app/controllers/keys_controller.rb index 949f2c0..fb28901 100644 --- a/users/app/controllers/keys_controller.rb +++ b/users/app/controllers/keys_controller.rb @@ -1,12 +1,18 @@ class KeysController < ApplicationController + # + # Render the user's key as plain text, without a layout. + # + # We will show blank page if user doesn't have key (which shouldn't generally occur) + # and a 404 error if user doesn't exist + # def show user = User.find_by_login(params[:login]) - # layout won't be included if we render text - # we will show blank page if user doesn't have key (which shouldn't generally occur) - # and a 404 error if user doesn't exist - user ? (render text: user.public_key) : (raise ActionController::RoutingError.new('Not Found')) - + if user + render text: user.public_key, content_type: 'text/text' + else + raise ActionController::RoutingError.new('Not Found') + end end end -- cgit v1.2.3 From d6cd73bca7b2e368d5c044a0bb473a89b88aaf2f Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 9 Dec 2013 19:58:20 +0100 Subject: update couchrest session store and Gemfile.lock This will fix the crash when loading the landing page without a couch connection. --- Gemfile.lock | 22 +++++++++++----------- core/leap_web_core.gemspec | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8d80546..e0aba9d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,38 +8,38 @@ GIT PATH remote: billing specs: - leap_web_billing (0.2.5) + leap_web_billing (0.2.8) braintree - leap_web_core (= 0.2.5) + leap_web_core (= 0.2.8) PATH remote: certs specs: - leap_web_certs (0.2.5) + leap_web_certs (0.2.8) certificate_authority (>= 0.2.0) - leap_web_core (= 0.2.5) + leap_web_core (= 0.2.8) PATH remote: core specs: - leap_web_core (0.2.5) + leap_web_core (0.2.8) couchrest (~> 1.1.3) couchrest_model (~> 2.0.0) - couchrest_session_store (~> 0.2.1) + couchrest_session_store (~> 0.2.2) json rails (~> 3.2.11) PATH remote: help specs: - leap_web_help (0.2.5) - leap_web_core (= 0.2.5) + leap_web_help (0.2.8) + leap_web_core (= 0.2.8) PATH remote: users specs: - leap_web_users (0.2.5) - leap_web_core (= 0.2.5) + leap_web_users (0.2.8) + leap_web_core (= 0.2.8) rails_warden ruby-srp (~> 0.2.1) @@ -110,7 +110,7 @@ GEM couchrest (~> 1.1.3) mime-types (>= 1.15) tzinfo (>= 0.3.22) - couchrest_session_store (0.2.1) + couchrest_session_store (0.2.2) actionpack couchrest couchrest_model diff --git a/core/leap_web_core.gemspec b/core/leap_web_core.gemspec index c745f00..4109a03 100644 --- a/core/leap_web_core.gemspec +++ b/core/leap_web_core.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.add_dependency "couchrest", "~> 1.1.3" s.add_dependency "couchrest_model", "~> 2.0.0" - s.add_dependency "couchrest_session_store", "~> 0.2.1" + s.add_dependency "couchrest_session_store", "~> 0.2.2" s.add_dependency "json" end -- cgit v1.2.3 From 0f429901318051f3d60050e4ca46e014e965b7d3 Mon Sep 17 00:00:00 2001 From: Azul Date: Mon, 9 Dec 2013 20:28:20 +0100 Subject: update ruby_core_source so it works with latest ruby version --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e0aba9d..d1fd014 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,7 +120,7 @@ GEM debugger-linecache (~> 1.2.0) debugger-ruby_core_source (~> 1.2.3) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.2.3) + debugger-ruby_core_source (1.2.4) erubis (2.7.0) eventmachine (1.0.3) execjs (2.0.2) -- cgit v1.2.3 From ce1d6ddacca09062ed90a40de559454c7ce912b5 Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 9 Dec 2013 11:50:47 -0800 Subject: Update tests to reflect using plaintext key. --- users/test/functional/keys_controller_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/users/test/functional/keys_controller_test.rb b/users/test/functional/keys_controller_test.rb index b69cbc0..863be93 100644 --- a/users/test/functional/keys_controller_test.rb +++ b/users/test/functional/keys_controller_test.rb @@ -8,7 +8,7 @@ class KeysControllerTest < ActionController::TestCase User.stubs(:find_by_login).with(@user.login).returns(@user) get :show, :login => @user.login assert_response :success - assert_equal "text/html", response.content_type + assert_equal "text/text", response.content_type assert_equal public_key, response.body end @@ -18,7 +18,7 @@ class KeysControllerTest < ActionController::TestCase User.stubs(:find_by_login).with(@user.login).returns(@user) get :show, :login => @user.login assert_response :success - assert_equal "text/html", response.content_type + assert_equal "text/text", response.content_type assert_equal '', response.body.strip end -- cgit v1.2.3 From e3b48ee60194b58c98cded485df4d936c5a4779d Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 12 Dec 2013 10:13:50 +0100 Subject: use the latest couchrest_session_store This one does not use our own error class for connection issues anymore. We'll remove that class in the next commit. So let's not rely on it. --- Gemfile.lock | 10 +++++----- core/leap_web_core.gemspec | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d1fd014..1c24bc6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ PATH leap_web_core (0.2.8) couchrest (~> 1.1.3) couchrest_model (~> 2.0.0) - couchrest_session_store (~> 0.2.2) + couchrest_session_store (~> 0.2.3) json rails (~> 3.2.11) @@ -105,12 +105,12 @@ GEM mime-types (~> 1.15) multi_json (~> 1.0) rest-client (~> 1.6.1) - couchrest_model (2.0.0) + couchrest_model (2.0.1) activemodel (>= 3.0) couchrest (~> 1.1.3) mime-types (>= 1.15) tzinfo (>= 0.3.22) - couchrest_session_store (0.2.2) + couchrest_session_store (0.2.3) actionpack couchrest couchrest_model @@ -145,7 +145,7 @@ GEM haml (~> 3.1) railties (>= 3.1, < 4.1) hike (1.2.3) - i18n (0.6.5) + i18n (0.6.9) journey (1.0.4) jquery-rails (3.0.4) railties (>= 3.0, < 5.0) @@ -162,7 +162,7 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) metaclass (0.0.1) - mime-types (1.25) + mime-types (1.25.1) mini_portile (0.5.1) mocha (0.13.3) metaclass (~> 0.0.1) diff --git a/core/leap_web_core.gemspec b/core/leap_web_core.gemspec index 4109a03..05874b2 100644 --- a/core/leap_web_core.gemspec +++ b/core/leap_web_core.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.add_dependency "couchrest", "~> 1.1.3" s.add_dependency "couchrest_model", "~> 2.0.0" - s.add_dependency "couchrest_session_store", "~> 0.2.2" + s.add_dependency "couchrest_session_store", "~> 0.2.3" s.add_dependency "json" end -- cgit v1.2.3 From 8fba6fae51e96ec3330896d107ab01fce2e322d9 Mon Sep 17 00:00:00 2001 From: Azul Date: Thu, 12 Dec 2013 10:15:43 +0100 Subject: reraise with a better explaination on couch failure Removing our own error class for this. It interferes with couch_rest_session_store tryign to catch the same errors. --- core/lib/extensions/couchrest.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/lib/extensions/couchrest.rb b/core/lib/extensions/couchrest.rb index 84cfbb3..7450f59 100644 --- a/core/lib/extensions/couchrest.rb +++ b/core/lib/extensions/couchrest.rb @@ -23,10 +23,6 @@ module CouchRest end end - module Errors - class ConnectionFailed < CouchRestModelError; end - end - module Connection module ClassMethods @@ -36,7 +32,9 @@ module CouchRest rescue RestClient::Unauthorized, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e - raise CouchRest::Model::Errors::ConnectionFailed.new(e.to_s) + message = "Could not connect to couch database #{db} due to #{e.to_s}" + Rails.logger.warn message + raise e.class.new(message) end end -- cgit v1.2.3 From ce99e9892dc5f807e072be8161c19f4969b1dace Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 13 Dec 2013 11:13:08 +0100 Subject: minor: rename test --- test/integration/home_test.rb | 24 ------------------------ test/integration/os_detection_test.rb | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 24 deletions(-) delete mode 100644 test/integration/home_test.rb create mode 100644 test/integration/os_detection_test.rb diff --git a/test/integration/home_test.rb b/test/integration/home_test.rb deleted file mode 100644 index 126a420..0000000 --- a/test/integration/home_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'test_helper' - -class AccountTest < BrowserIntegrationTest - - setup do - Capybara.current_driver = Capybara.javascript_driver - end - - test "old windows shows deactivated download" do - page.driver.headers = { "User-Agent" => "Win98" } - visit '/' - assert_selector "html.oldwin" - assert has_text? "not available" - end - - test "android shows android download" do - page.driver.headers = { "User-Agent" => "Android" } - visit '/' - assert_selector "html.android" - assert has_no_text? "not available" - assert_selector "small", text: "Android" - end - -end diff --git a/test/integration/os_detection_test.rb b/test/integration/os_detection_test.rb new file mode 100644 index 0000000..cb254aa --- /dev/null +++ b/test/integration/os_detection_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class OsDetectionTest < BrowserIntegrationTest + + setup do + Capybara.current_driver = Capybara.javascript_driver + end + + test "old windows shows deactivated download" do + page.driver.headers = { "User-Agent" => "Win98" } + visit '/' + assert_selector "html.oldwin" + assert has_text? "not available" + end + + test "android shows android download" do + page.driver.headers = { "User-Agent" => "Android" } + visit '/' + assert_selector "html.android" + assert has_no_text? "not available" + assert_selector "small", text: "Android" + end + +end -- cgit v1.2.3 From 72087656e5092fd744f4314c9a0e91825399fefc Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 13 Dec 2013 11:16:11 +0100 Subject: proceed even if the couch is unreachable * in case the user has a session id, keep it but proceed without a session * in case we can't initialize the models proceed * if APP_CONFIG[:reraise_errors] is set we'll crash instead in the latter case default to reraise errors in dev and test environments. --- Gemfile.lock | 4 ++-- config/defaults.yml | 2 ++ core/leap_web_core.gemspec | 2 +- core/lib/extensions/couchrest.rb | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1c24bc6..918fdba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ PATH leap_web_core (0.2.8) couchrest (~> 1.1.3) couchrest_model (~> 2.0.0) - couchrest_session_store (~> 0.2.3) + couchrest_session_store (~> 0.2.4) json rails (~> 3.2.11) @@ -110,7 +110,7 @@ GEM couchrest (~> 1.1.3) mime-types (>= 1.15) tzinfo (>= 0.3.22) - couchrest_session_store (0.2.3) + couchrest_session_store (0.2.4) actionpack couchrest couchrest_model diff --git a/config/defaults.yml b/config/defaults.yml index 4530d47..4bd8eec 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -72,6 +72,7 @@ development: domain: example.org secret_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' payment: [] + reraise_errors: true test: <<: *downloads @@ -83,6 +84,7 @@ test: domain: test.me secret_token: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' payment: [billing] + reraise_errors: true production: <<: *downloads diff --git a/core/leap_web_core.gemspec b/core/leap_web_core.gemspec index 05874b2..7ca4d90 100644 --- a/core/leap_web_core.gemspec +++ b/core/leap_web_core.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.add_dependency "couchrest", "~> 1.1.3" s.add_dependency "couchrest_model", "~> 2.0.0" - s.add_dependency "couchrest_session_store", "~> 0.2.3" + s.add_dependency "couchrest_session_store", "~> 0.2.4" s.add_dependency "json" end diff --git a/core/lib/extensions/couchrest.rb b/core/lib/extensions/couchrest.rb index 7450f59..9f27c3a 100644 --- a/core/lib/extensions/couchrest.rb +++ b/core/lib/extensions/couchrest.rb @@ -34,7 +34,7 @@ module CouchRest Errno::ECONNREFUSED => e message = "Could not connect to couch database #{db} due to #{e.to_s}" Rails.logger.warn message - raise e.class.new(message) + raise e.class.new(message) if APP_CONFIG[:reraise_errors] end end -- cgit v1.2.3 From b7db3e8f5d9a65ff3b83316bdd802dd40111c64d Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 13 Dec 2013 13:32:20 +0100 Subject: refactor: move edit sections into partials --- users/app/views/users/_change_password.html.haml | 21 ++++++ users/app/views/users/_change_pgp_key.html.haml | 13 ++++ .../views/users/_change_service_level.html.haml | 18 +++++ users/app/views/users/_destroy_account.html.haml | 27 +++++++ users/app/views/users/_edit.html.haml | 84 ++-------------------- 5 files changed, 83 insertions(+), 80 deletions(-) create mode 100644 users/app/views/users/_change_password.html.haml create mode 100644 users/app/views/users/_change_pgp_key.html.haml create mode 100644 users/app/views/users/_change_service_level.html.haml create mode 100644 users/app/views/users/_destroy_account.html.haml diff --git a/users/app/views/users/_change_password.html.haml b/users/app/views/users/_change_password.html.haml new file mode 100644 index 0000000..425e3ee --- /dev/null +++ b/users/app/views/users/_change_password.html.haml @@ -0,0 +1,21 @@ +-# +-# CHANGE PASSWORD +-# +-# * everything about this form is handled with javascript. So take care when changing any ids. +-# * the login is required when changing the password because it is used as part of the salt when calculating the password verifier. +-# however, we don't want the user to change their login without generating a new key, so we hide the ui for this +-# (although it works perfectly fine to change username if the field was visible). +-# + +- form_options = {:url => '/not-used', :html => {:class => user_form_class('form-horizontal'), :id => 'update_login_and_password', :data => {token: session[:token]}}, :validate => true} += simple_form_for @user, form_options do |f| + %legend= t(:change_password) + = hidden_field_tag 'user_param', @user.to_param + .hidden + = f.input :login, :label => t(:username), :required => false, :input_html => {:id => :srp_username} + = f.input :password, :required => false, :validate => true, :input_html => { :id => :srp_password } + = f.input :password_confirmation, :required => false, :input_html => { :id => :srp_password_confirmation } + .control-group + .controls + = f.submit t(:save), :class => 'btn btn-primary' + diff --git a/users/app/views/users/_change_pgp_key.html.haml b/users/app/views/users/_change_pgp_key.html.haml new file mode 100644 index 0000000..e465125 --- /dev/null +++ b/users/app/views/users/_change_pgp_key.html.haml @@ -0,0 +1,13 @@ +-# +-# CHANGE PGP KEY +-# +-# this will be replaced by a identities controller/view at some point +-# + +- form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_pgp_key', :data => {token: session[:token]}}, :validate => true} += simple_form_for [:api, @user], form_options do |f| + %legend= t(:advanced_options) + = f.input :public_key, :as => :text, :hint => t(:use_ascii_key), :input_html => {:class => "full-width", :rows => 4} + .control-group + .controls + = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} diff --git a/users/app/views/users/_change_service_level.html.haml b/users/app/views/users/_change_service_level.html.haml new file mode 100644 index 0000000..61e67d9 --- /dev/null +++ b/users/app/views/users/_change_service_level.html.haml @@ -0,0 +1,18 @@ +-# TODO: probably won't want here, but here for now. Also, we will need way to ensure payment if they pick a non-free plan. +-# +-# SERVICE LEVEL +-# +- if APP_CONFIG[:service_levels] + - form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_service_level', :data => {token: session[:token]}}, :validate => true} + = simple_form_for @user, form_options do |f| + %legend= t(:service_level) + - if @user != current_user + = t(:desired_service_level) + = f.select :desired_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.desired_service_level.id + - if @user != current_user + %p + = t(:effective_service_level) + = f.select :effective_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.effective_service_level.id + .control-group + .controls + = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} diff --git a/users/app/views/users/_destroy_account.html.haml b/users/app/views/users/_destroy_account.html.haml new file mode 100644 index 0000000..445f3c4 --- /dev/null +++ b/users/app/views/users/_destroy_account.html.haml @@ -0,0 +1,27 @@ +-# +-# DESTROY ACCOUNT +-# + +%legend + - if @user == current_user + = t(:destroy_my_account) + - else + = t(:admin_destroy_account, :username => @user.login) +%p= t(:destroy_account_info) += link_to user_path(@user), :method => :delete, :confirm => t(:are_you_sure), :class => "btn btn-danger" do + %i.icon-remove.icon-white + = t(:destroy_my_account) +- if @user != current_user and @user.enabled? + %legend + = t(:deactivate_account, :username => @user.login) + %p= t(:deactivate_description) + = link_to deactivate_user_path(@user), :method => :post, :class => "btn btn-warning" do + %i.icon-pause.icon-white + = t(:deactivate) +- elsif @user != current_user and !@user.enabled? + %legend + = t(:enable_account, :username => @user.login) + %p= t(:enable_description) + = link_to enable_user_path(@user), :method => :post, :class => "btn btn-warning" do + %i.icon-ok.icon-white + = t(:enable) diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index 0b36d6e..b52da3d 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -2,84 +2,8 @@ -# edit user form, used by both show and edit actions. -# --# --# CHANGE PASSWORD --# --# * everything about this form is handled with javascript. So take care when changing any ids. --# * the login is required when changing the password because it is used as part of the salt when calculating the password verifier. --# however, we don't want the user to change their login without generating a new key, so we hide the ui for this --# (although it works perfectly fine to change username if the field was visible). --# - -- form_options = {:url => '/not-used', :html => {:class => user_form_class('form-horizontal'), :id => 'update_login_and_password', :data => {token: session[:token]}}, :validate => true} -= simple_form_for @user, form_options do |f| - %legend= t(:change_password) - = hidden_field_tag 'user_param', @user.to_param - .hidden - = f.input :login, :label => t(:username), :required => false, :input_html => {:id => :srp_username} - = f.input :password, :required => false, :validate => true, :input_html => { :id => :srp_password } - = f.input :password_confirmation, :required => false, :input_html => { :id => :srp_password_confirmation } - .control-group - .controls - = f.submit t(:save), :class => 'btn btn-primary' - --# --# CHANGE PGP KEY --# --# this will be replaced by a identities controller/view at some point --# - -- form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_pgp_key', :data => {token: session[:token]}}, :validate => true} -= simple_form_for [:api, @user], form_options do |f| - %legend= t(:advanced_options) - = f.input :public_key, :as => :text, :hint => t(:use_ascii_key), :input_html => {:class => "full-width", :rows => 4} - .control-group - .controls - = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} - - --# TODO: probably won't want here, but here for now. Also, we will need way to ensure payment if they pick a non-free plan. --# --# SERVICE LEVEL --# -- if APP_CONFIG[:service_levels] - - form_options = {:html => {:class => user_form_class('form-horizontal'), :id => 'update_service_level', :data => {token: session[:token]}}, :validate => true} - = simple_form_for @user, form_options do |f| - %legend= t(:service_level) - - if @user != current_user - = t(:desired_service_level) - = f.select :desired_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.desired_service_level.id - - if @user != current_user - %p - = t(:effective_service_level) - = f.select :effective_service_level_code, ServiceLevel.authenticated_select_options, :selected => @user.effective_service_level.id - .control-group - .controls - = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."} --# --# DESTROY ACCOUNT --# += render 'change_password' += render 'change_pgp_key' += render 'change_service_level' += render 'destroy_account' -%legend - - if @user == current_user - = t(:destroy_my_account) - - else - = t(:admin_destroy_account, :username => @user.login) -%p= t(:destroy_account_info) -= link_to user_path(@user), :method => :delete, :confirm => t(:are_you_sure), :class => "btn btn-danger" do - %i.icon-remove.icon-white - = t(:destroy_my_account) -- if @user != current_user and @user.enabled? - %legend - = t(:deactivate_account, :username => @user.login) - %p= t(:deactivate_description) - = link_to deactivate_user_path(@user), :method => :post, :class => "btn btn-warning" do - %i.icon-pause.icon-white - = t(:deactivate) -- elsif @user != current_user and !@user.enabled? - %legend - = t(:enable_account, :username => @user.login) - %p= t(:enable_description) - = link_to enable_user_path(@user), :method => :post, :class => "btn btn-warning" do - %i.icon-ok.icon-white - = t(:enable) -- cgit v1.2.3 From 5ed00a63ea3da98e8dfaef88752f8afe851c81e9 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 13 Dec 2013 14:10:41 +0100 Subject: make the possible actions for users and admins configurable with tests --- config/defaults.yml | 4 ++ users/app/views/users/_edit.html.haml | 17 ++++--- users/test/integration/browser/account_test.rb | 66 +++++++++++++++++--------- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/config/defaults.yml b/config/defaults.yml index 4530d47..283f406 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -40,6 +40,10 @@ common: &common handle_blacklist: [certmaster, ssladmin, arin-admin, administrator, www-data, maildrop] # handles that will be allowed despite being in /etc/passwd or rfc2142 handle_whitelist: [] + # actions enabled in the account settings + # see /users/app/views/users/_edit.html.haml for a list. + user_actions: ['destroy_account'] + admin_actions: ['change_pgp_key', 'change_service_level', 'destroy_account'] service_levels: &service_levels service_levels: diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml index b52da3d..1d2b68a 100644 --- a/users/app/views/users/_edit.html.haml +++ b/users/app/views/users/_edit.html.haml @@ -1,9 +1,14 @@ -# -# edit user form, used by both show and edit actions. -# - -= render 'change_password' -= render 'change_pgp_key' -= render 'change_service_level' -= render 'destroy_account' - +-# We render a bunch of forms here. Which we use depends upon config settings +-# user_actions and admin_actions. They both include an array of actions +-# allowed to users and admins. +-# Possible forms are: +-# 'change_password' +-# 'change_pgp_key' +-# 'change_service_level' +-# 'destroy_account' +- actions = APP_CONFIG[admin? ? :admin_actions : :user_actions] || [] +- actions.each do |action| + = render action diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb index 3d281ae..4cefe35 100644 --- a/users/test/integration/browser/account_test.rb +++ b/users/test/integration/browser/account_test.rb @@ -51,35 +51,57 @@ class AccountTest < BrowserIntegrationTest assert page.has_content?('has already been taken') end - test "change password" do + test "default user actions" do username, password = submit_signup click_on "Account Settings" - within('#update_login_and_password') do - fill_in 'Password', with: "other password" - fill_in 'Password confirmation', with: "other password" - click_on 'Save' + assert page.has_content? I18n.t('destroy_my_account') + assert page.has_no_css? '#update_login_and_password' + assert page.has_no_css? '#update_pgp_key' + end + + test "default admin actions" do + username, password = submit_signup + with_config admins: [username] do + click_on "Account Settings" + assert page.has_content? I18n.t('destroy_my_account') + assert page.has_no_css? '#update_login_and_password' + assert page.has_css? '#update_pgp_key' + end + end + + test "change password" do + with_config user_actions: ['change_password'] do + username, password = submit_signup + click_on "Account Settings" + within('#update_login_and_password') do + fill_in 'Password', with: "other password" + fill_in 'Password confirmation', with: "other password" + click_on 'Save' + end + click_on 'Logout' + attempt_login(username, "other password") + assert page.has_content?("Welcome #{username}") + User.find_by_login(username).account.destroy end - click_on 'Logout' - attempt_login(username, "other password") - assert page.has_content?("Welcome #{username}") - User.find_by_login(username).account.destroy end test "change pgp key" do - pgp_key = FactoryGirl.build :pgp_key - username, password = submit_signup - click_on "Account Settings" - within('#update_pgp_key') do - fill_in 'Public key', with: pgp_key - click_on 'Save' + with_config user_actions: ['change_pgp_key'] do + pgp_key = FactoryGirl.build :pgp_key + username, password = submit_signup + click_on "Account Settings" + within('#update_pgp_key') do + fill_in 'Public key', with: pgp_key + click_on 'Save' + end + page.assert_selector 'input[value="Saving..."]' + # at some point we're done: + page.assert_no_selector 'input[value="Saving..."]' + assert page.has_field? 'Public key', with: pgp_key.to_s + user = User.find_by_login(username) + assert_equal pgp_key, user.public_key + user.account.destroy end - page.assert_selector 'input[value="Saving..."]' - # at some point we're done: - page.assert_no_selector 'input[value="Saving..."]' - assert page.has_field? 'Public key', with: pgp_key.to_s - user = User.find_by_login(username) - assert_equal pgp_key, user.public_key - user.account.destroy end -- cgit v1.2.3 From 496817bd512fe43c4cb80cc49a19dae3ed3eb165 Mon Sep 17 00:00:00 2001 From: Azul Date: Fri, 13 Dec 2013 14:27:15 +0100 Subject: refactor: remove Overview controller - we can use Users#show we were only using Users#show to redirect to the edit action. So I replaced that with the overview and we have no more use for the extra controller. This also simplifies linking to the users in question a lot. --- app/controllers/home_controller.rb | 2 +- app/views/layouts/_navigation.html.haml | 2 +- help/app/views/tickets/_edit_form.html.haml | 4 ++-- users/app/controllers/overviews_controller.rb | 9 --------- users/app/controllers/sessions_controller.rb | 4 ++-- users/app/controllers/users_controller.rb | 2 +- users/app/views/overviews/show.html.haml | 22 ---------------------- users/app/views/users/_user.html.haml | 2 +- users/app/views/users/show.html.haml | 23 ++++++++++++++++++++++- users/config/routes.rb | 3 +-- 10 files changed, 31 insertions(+), 42 deletions(-) delete mode 100644 users/app/controllers/overviews_controller.rb delete mode 100644 users/app/views/overviews/show.html.haml diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index be26eb6..1d62178 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -3,7 +3,7 @@ class HomeController < ApplicationController def index if logged_in? - redirect_to user_overview_url(current_user) + redirect_to current_user end end end diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml index 992aa46..6de567a 100644 --- a/app/views/layouts/_navigation.html.haml +++ b/app/views/layouts/_navigation.html.haml @@ -1,5 +1,5 @@ %ul.nav.sidenav - = link_to_navigation t(:overview), user_overview_path(@user), :active => controller?(:overviews) + = link_to_navigation t(:overview), @user, :active => controller?(:overviews) = link_to_navigation t(:account_settings), edit_user_path(@user), :active => controller?(:users) - # will want link for identity settings = link_to_navigation t(:support_tickets), auto_tickets_path, :active => controller?(:tickets) diff --git a/help/app/views/tickets/_edit_form.html.haml b/help/app/views/tickets/_edit_form.html.haml index fa0ab44..714f8ff 100644 --- a/help/app/views/tickets/_edit_form.html.haml +++ b/help/app/views/tickets/_edit_form.html.haml @@ -1,7 +1,7 @@ :ruby # created by user link if @ticket.created_by_user - created_by = link_to(@ticket.created_by_user.login, user_overview_path(@ticket.created_by_user)) + created_by = link_to @ticket.created_by_user.login, @ticket.created_by_user else created_by = t(:anonymous) end @@ -9,7 +9,7 @@ # regarding user link if admin? if @ticket.regarding_user_actual_user - regarding_user_link = link_to @ticket.regarding_user_actual_user.login, user_overview_path(@ticket.regarding_user_actual_user) + regarding_user_link = link_to @ticket.regarding_user_actual_user.login, @ticket.regarding_user_actual_user else regarding_user_link = "(#{t(:unknown)})" end diff --git a/users/app/controllers/overviews_controller.rb b/users/app/controllers/overviews_controller.rb deleted file mode 100644 index 52ce267..0000000 --- a/users/app/controllers/overviews_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -class OverviewsController < UsersBaseController - - before_filter :authorize - before_filter :fetch_user - - def show - end - -end diff --git a/users/app/controllers/sessions_controller.rb b/users/app/controllers/sessions_controller.rb index 0494b51..85a022e 100644 --- a/users/app/controllers/sessions_controller.rb +++ b/users/app/controllers/sessions_controller.rb @@ -14,12 +14,12 @@ class SessionsController < ApplicationController end # - # this is a bad hack, but user_overview_url(user) is not available + # this is a bad hack, but user_url(user) is not available # also, this doesn't work because the redirect happens as a PUT. no idea why. # #Warden::Manager.after_authentication do |user, auth, opts| # response = Rack::Response.new - # response.redirect "/users/#{user.id}/overview" + # response.redirect "/users/#{user.id}" # throw :warden, response.finish #end diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index 8b4715c..0b32ec7 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -13,7 +13,7 @@ class UsersController < UsersBaseController def index if params[:query] if @user = User.find_by_login(params[:query]) - redirect_to user_overview_url(@user) + redirect_to @user return else @users = User.by_login.startkey(params[:query]).endkey(params[:query].succ) diff --git a/users/app/views/overviews/show.html.haml b/users/app/views/overviews/show.html.haml deleted file mode 100644 index 7bea370..0000000 --- a/users/app/views/overviews/show.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -.overview - - %h2.first= t(:overview_welcome, :username => @user.login) - - - if admin? - %p - = t(:created) - = @user.created_at - %br - = t(:updated) - = @user.updated_at - %br - = t(:enabled) - = @user.enabled? - - %p= t(:overview_intro) - - %ul.unstyled - %li= icon('user') + link_to(t(:overview_account), edit_user_path(@user)) - - # %li= icon('envelope') + link_to(t(:overview_email), {insert path for user identities, presuambly} - %li= icon('question-sign') + link_to(t(:overview_tickets), user_tickets_path(@user)) - %li= icon('shopping-cart') + link_to(t(:overview_billing), billing_top_link(@user)) if APP_CONFIG[:payment].present? diff --git a/users/app/views/users/_user.html.haml b/users/app/views/users/_user.html.haml index 990d9cf..583d22f 100644 --- a/users/app/views/users/_user.html.haml +++ b/users/app/views/users/_user.html.haml @@ -1,4 +1,4 @@ %tr - %td= link_to user.login, user_overview_path(user) + %td= link_to user.login, user %td= l(user.created_at, :format => :short) %td= l(user.updated_at, :format => :short) diff --git a/users/app/views/users/show.html.haml b/users/app/views/users/show.html.haml index 434c025..7bea370 100644 --- a/users/app/views/users/show.html.haml +++ b/users/app/views/users/show.html.haml @@ -1 +1,22 @@ -= render 'edit' +.overview + + %h2.first= t(:overview_welcome, :username => @user.login) + + - if admin? + %p + = t(:created) + = @user.created_at + %br + = t(:updated) + = @user.updated_at + %br + = t(:enabled) + = @user.enabled? + + %p= t(:overview_intro) + + %ul.unstyled + %li= icon('user') + link_to(t(:overview_account), edit_user_path(@user)) + - # %li= icon('envelope') + link_to(t(:overview_email), {insert path for user identities, presuambly} + %li= icon('question-sign') + link_to(t(:overview_tickets), user_tickets_path(@user)) + %li= icon('shopping-cart') + link_to(t(:overview_billing), billing_top_link(@user)) if APP_CONFIG[:payment].present? diff --git a/users/config/routes.rb b/users/config/routes.rb index 69f9cf7..de2ff37 100644 --- a/users/config/routes.rb +++ b/users/config/routes.rb @@ -13,9 +13,8 @@ Rails.application.routes.draw do get "signup" => "users#new", :as => "signup" resources :users, :except => [:create, :update] do - resource :overview, :only => [:show] # resource :email_settings, :only => [:edit, :update] - resources :email_aliases, :only => [:destroy], :id => /.*/ + # resources :email_aliases, :only => [:destroy], :id => /.*/ post 'deactivate', on: :member post 'enable', on: :member end -- cgit v1.2.3 From 6012b588911dac3d47bbdb53e11dcb70ba8be6df Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 16 Dec 2013 11:37:05 -0800 Subject: Fix issue 4756: /login should not crash if one goes there when logged in. --- users/app/controllers/sessions_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/users/app/controllers/sessions_controller.rb b/users/app/controllers/sessions_controller.rb index 85a022e..ca228c2 100644 --- a/users/app/controllers/sessions_controller.rb +++ b/users/app/controllers/sessions_controller.rb @@ -1,6 +1,7 @@ class SessionsController < ApplicationController def new + redirect_to root_path if logged_in? @session = Session.new if authentication_errors @errors = authentication_errors -- cgit v1.2.3 From 91aea91d7091b740630551ec17d0274236545f4c Mon Sep 17 00:00:00 2001 From: jessib Date: Mon, 16 Dec 2013 11:49:16 -0800 Subject: Add test. --- users/test/functional/sessions_controller_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/users/test/functional/sessions_controller_test.rb b/users/test/functional/sessions_controller_test.rb index 28143da..8b49005 100644 --- a/users/test/functional/sessions_controller_test.rb +++ b/users/test/functional/sessions_controller_test.rb @@ -17,6 +17,13 @@ class SessionsControllerTest < ActionController::TestCase assert_template "sessions/new" end + test "redirect to root_url if logged in" do + login + get :new + assert_response :redirect + assert_redirected_to root_url + end + test "renders json" do get :new, :format => :json assert_response :success -- cgit v1.2.3 From 95bd5d46095c3552a31cf719adab1c84971b95e8 Mon Sep 17 00:00:00 2001 From: Azul Date: Tue, 17 Dec 2013 11:37:37 +0100 Subject: catch all rest client exceptions during initialization --- core/lib/extensions/couchrest.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/extensions/couchrest.rb b/core/lib/extensions/couchrest.rb index 9f27c3a..a9a195e 100644 --- a/core/lib/extensions/couchrest.rb +++ b/core/lib/extensions/couchrest.rb @@ -29,7 +29,7 @@ module CouchRest def use_database(db) @database = prepare_database(db) - rescue RestClient::Unauthorized, + rescue RestClient::Exception, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e message = "Could not connect to couch database #{db} due to #{e.to_s}" -- cgit v1.2.3