summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--billing/README.rdoc11
-rw-r--r--config/application.rb2
-rw-r--r--config/environments/production.rb10
m---------users/app/assets/javascripts/srp0
-rw-r--r--users/app/assets/javascripts/users.js31
-rw-r--r--users/app/controllers/v1/sessions_controller.rb1
-rw-r--r--users/app/models/email.rb6
-rw-r--r--users/app/models/identity.rb18
-rw-r--r--users/app/models/local_email.rb4
-rw-r--r--users/app/models/login_format_validation.rb8
-rw-r--r--users/app/views/users/_edit.html.haml7
-rw-r--r--users/lib/warden/strategies/secure_remote_password.rb1
-rw-r--r--users/test/integration/browser/account_test.rb36
-rw-r--r--users/test/unit/identity_test.rb20
14 files changed, 132 insertions, 23 deletions
diff --git a/billing/README.rdoc b/billing/README.rdoc
index 357c02e..30ca0d6 100644
--- a/billing/README.rdoc
+++ b/billing/README.rdoc
@@ -2,18 +2,19 @@
This project rocks and uses MIT-LICENSE.
+The gem leap_web_billing will need to be included in whatever environment you are running, and billing will also need to be included in the configuration for that environment. You can set billing to be included in config/defaults.yml, by making sure the payment key is set to an array including billing (by default it will be set for the test environment, so you can look at that.)
+
To set up your own Braintree Sandbox, create an account at:
https://www.braintreepayments.com/get-started
-Login.
+Login to the Braintree Sandbox.
In the top right, navigate to your username, and then 'My User' -> 'API Keys'
Click the button to generate a new API key, and then click the 'View' link to the right of the key.
-There is a section to copy a snippet of code. Select 'Ruby' in the dropdown, and then the button to the right to copy this code to your clipboard.
-Then, paste the contents of the clipboard into config/initializers/braintree.rb
-
-You should not check the private key into version control.
+There is a section to copy a snippet of code. The simplest way to get this working is to select 'Ruby' in the dropdown, and then the button to the right to copy this code to your clipboard, and then paste the contents of the clipboard into billing/config/initializers/braintree.rb
+However, you should not check the private key into version control, so you should not check in this file.
+The better way to do this is to leave billing/config/initializers/braintree.rb as is, and instead set the braintree variables for the appropriate environment in config/config.yml, which is excluded from version control.
Now, you should be able to add charges to your own Sandbox when you run the webapp locally.
diff --git a/config/application.rb b/config/application.rb
index e8bb2f4..8587ffc 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -54,7 +54,7 @@ module LeapWeb
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
- if APP_CONFIG[:logfile]
+ if APP_CONFIG[:logfile].present?
config.logger = Logger.new(APP_CONFIG[:logfile])
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 32b4558..7acca75 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -33,11 +33,11 @@ LeapWeb::Application.configure do
# See everything in the log (default is :info)
# config.log_level = :debug
- # Prepend all log lines with the following tags
- # config.log_tags = [ :subdomain, :uuid ]
-
- # Use a different logger for distributed setups
- # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
+ # Use syslog if no file has been specified
+ if APP_CONFIG[:logfile].blank?
+ require 'syslog/logger'
+ config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new('webapp'))
+ end
# Use a different cache store in production
# config.cache_store = :mem_cache_store
diff --git a/users/app/assets/javascripts/srp b/users/app/assets/javascripts/srp
-Subproject 9c61d52f1f975ec0eefe5b4a0b71ac529300cbe
+Subproject d22bf3b9fe2fd31192e1e1b358e97e5a0f3f90b
diff --git a/users/app/assets/javascripts/users.js b/users/app/assets/javascripts/users.js
index 4c9b510..aaeba6e 100644
--- a/users/app/assets/javascripts/users.js
+++ b/users/app/assets/javascripts/users.js
@@ -3,7 +3,12 @@
// LOCAL FUNCTIONS
//
- var poll_users, prevent_default, form_failed, form_passed, clear_errors;
+ var poll_users,
+ prevent_default,
+ form_failed,
+ form_passed,
+ clear_errors,
+ update_user;
prevent_default = function(event) {
return event.preventDefault();
@@ -19,6 +24,27 @@
return $('#messages').empty();
};
+ update_user = function(submitEvent) {
+ var form = submitEvent.target;
+ var token = form.dataset.token;
+ var url = form.action;
+ var req = $.ajax({
+ url: url,
+ type: 'PUT',
+ headers: { Authorization: 'Token token="' + token + '"' },
+ data: $(form).serialize()
+ });
+ req.done( function() {
+ $(form).find('input[type="submit"]').button('reset');
+ });
+ };
+
+ markAsSubmitted = function(submitEvent) {
+ var form = submitEvent.target;
+ $(form).addClass('submitted')
+ // bootstrap loading state:
+ $(form).find('input[type="submit"]').button('loading');
+ };
//
// PUBLIC FUNCTIONS
@@ -70,12 +96,15 @@
//
$(document).ready(function() {
+ $('form').submit(markAsSubmitted);
$('#new_user').submit(prevent_default);
$('#new_user').submit(srp.signup);
$('#new_session').submit(prevent_default);
$('#new_session').submit(srp.login);
$('#update_login_and_password').submit(prevent_default);
$('#update_login_and_password').submit(srp.update);
+ $('#update_pgp_key').submit(prevent_default);
+ $('#update_pgp_key').submit(update_user);
return $('#user-typeahead').typeahead({
source: poll_users
});
diff --git a/users/app/controllers/v1/sessions_controller.rb b/users/app/controllers/v1/sessions_controller.rb
index 1b20a82..eb6c322 100644
--- a/users/app/controllers/v1/sessions_controller.rb
+++ b/users/app/controllers/v1/sessions_controller.rb
@@ -24,6 +24,7 @@ module V1
def update
authenticate!
@token = Token.create(:user_id => current_user.id)
+ session[:token] = @token.id
render :json => login_response
end
diff --git a/users/app/models/email.rb b/users/app/models/email.rb
index 1bcff1c..a9a503f 100644
--- a/users/app/models/email.rb
+++ b/users/app/models/email.rb
@@ -3,7 +3,7 @@ class Email < String
validates :email,
:format => {
- :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/,
+ :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/, #local part of email is case-sensitive, so allow uppercase letter.
:message => "needs to be a valid email address"
}
@@ -19,4 +19,8 @@ class Email < String
self
end
+ def handle
+ self.split('@').first
+ end
+
end
diff --git a/users/app/models/identity.rb b/users/app/models/identity.rb
index 355f67a..e0a24e9 100644
--- a/users/app/models/identity.rb
+++ b/users/app/models/identity.rb
@@ -1,4 +1,5 @@
class Identity < CouchRest::Model::Base
+ include LoginFormatValidation
use_database :identities
@@ -10,6 +11,8 @@ class Identity < CouchRest::Model::Base
validate :unique_forward
validate :alias_available
+ validate :address_local_email
+ validate :destination_email
design do
view :by_user_id
@@ -63,6 +66,11 @@ class Identity < CouchRest::Model::Base
write_attribute('keys', keys.merge(type => value))
end
+ # for LoginFormatValidation
+ def login
+ self.address.handle
+ end
+
protected
def unique_forward
@@ -79,4 +87,14 @@ class Identity < CouchRest::Model::Base
end
end
+ def address_local_email
+ return if address.valid? #this ensures it is LocalEmail
+ self.errors.add(:address, address.errors.messages[:email].first) #assumes only one error
+ end
+
+ def destination_email
+ return if destination.valid? #this ensures it is Email
+ self.errors.add(:destination, destination.errors.messages[:email].first) #assumes only one error #TODO
+ end
+
end
diff --git a/users/app/models/local_email.rb b/users/app/models/local_email.rb
index c1f7c11..6303bb6 100644
--- a/users/app/models/local_email.rb
+++ b/users/app/models/local_email.rb
@@ -20,10 +20,6 @@ class LocalEmail < Email
[handle]
end
- def handle
- gsub(/@#{domain}/i, '')
- end
-
def domain
LocalEmail.domain
end
diff --git a/users/app/models/login_format_validation.rb b/users/app/models/login_format_validation.rb
index 1d02bd1..c1fcf70 100644
--- a/users/app/models/login_format_validation.rb
+++ b/users/app/models/login_format_validation.rb
@@ -1,19 +1,21 @@
module LoginFormatValidation
extend ActiveSupport::Concern
+ #TODO: Probably will replace this. Playing with using it for aliases too, but won't want it connected to login field.
+
included do
# Have multiple regular expression validations so we can get specific error messages:
validates :login,
:format => { :with => /\A.{2,}\z/,
- :message => "Login must have at least two characters"}
+ :message => "Must have at least two characters"}
validates :login,
:format => { :with => /\A[a-z\d_\.-]+\z/,
:message => "Only lowercase letters, digits, . - and _ allowed."}
validates :login,
:format => { :with => /\A[a-z].*\z/,
- :message => "Login must begin with a lowercase letter"}
+ :message => "Must begin with a lowercase letter"}
validates :login,
:format => { :with => /\A.*[a-z\d]\z/,
- :message => "Login must end with a letter or digit"}
+ :message => "Must end with a letter or digit"}
end
end
diff --git a/users/app/views/users/_edit.html.haml b/users/app/views/users/_edit.html.haml
index 5f74d32..9d2473b 100644
--- a/users/app/views/users/_edit.html.haml
+++ b/users/app/views/users/_edit.html.haml
@@ -10,7 +10,8 @@
-# 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'}, :validate => true}
+
+- 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
@@ -28,13 +29,13 @@
-# 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'}, :validate => true}
+- 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'
+ = f.submit t(:save), :class => 'btn', :data => {"loading-text" => "Saving..."}
-#
-# DESTROY ACCOUNT
diff --git a/users/lib/warden/strategies/secure_remote_password.rb b/users/lib/warden/strategies/secure_remote_password.rb
index 4688fcd..2c334c6 100644
--- a/users/lib/warden/strategies/secure_remote_password.rb
+++ b/users/lib/warden/strategies/secure_remote_password.rb
@@ -31,6 +31,7 @@ module Warden
Rails.logger.warn "Login attempt failed."
Rails.logger.debug debug_info
Rails.logger.debug "Received: #{params['client_auth']}"
+ session.delete(:handshake)
fail!(:base => "invalid_user_pass")
end
end
diff --git a/users/test/integration/browser/account_test.rb b/users/test/integration/browser/account_test.rb
index 8c2c997..1deda45 100644
--- a/users/test/integration/browser/account_test.rb
+++ b/users/test/integration/browser/account_test.rb
@@ -24,8 +24,44 @@ class AccountTest < BrowserIntegrationTest
fill_in 'Password', with: password
click_on 'Log In'
assert page.has_content?("Welcome #{username}")
+ User.find_by_login(username).account.destroy
end
+ test "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'
+ click_on 'Log In'
+ fill_in 'Username', with: username
+ fill_in 'Password', with: "other password"
+ click_on 'Log In'
+ assert page.has_content?("Welcome #{username}")
+ User.find_by_login(username).account.destroy
+ end
+
+ test "change pgp key" do
+ pgp_key = "My PGP Key Stub"
+ 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
+ user = User.find_by_login(username)
+ assert_equal pgp_key, user.public_key
+ user.account.destroy
+ end
+
+
# trying to seed an invalid A for srp login
test "detects attempt to circumvent SRP" do
user = FactoryGirl.create :user
diff --git a/users/test/unit/identity_test.rb b/users/test/unit/identity_test.rb
index fa88315..0842a77 100644
--- a/users/test/unit/identity_test.rb
+++ b/users/test/unit/identity_test.rb
@@ -70,6 +70,26 @@ class IdentityTest < ActiveSupport::TestCase
id.destroy
end
+ test "fail to add non-local email address as identity address" do
+ id = Identity.for @user, address: forward_address
+ assert !id.valid?
+ assert_match /needs to end in/, id.errors[:address].first
+ end
+
+ test "alias must meet same conditions as login" do
+ id = Identity.create_for @user, address: alias_name.capitalize
+ assert !id.valid?
+ #hacky way to do this, but okay for now:
+ assert id.errors.messages.flatten(2).include? "Must begin with a lowercase letter"
+ assert id.errors.messages.flatten(2).include? "Only lowercase letters, digits, . - and _ allowed."
+ end
+
+ test "destination must be valid email address" do
+ id = Identity.create_for @user, address: @user.email_address, destination: 'ASKJDLFJD'
+ assert !id.valid?
+ assert id.errors.messages[:destination].include? "needs to be a valid email address"
+ end
+
def alias_name
@alias_name ||= Faker::Internet.user_name
end