diff options
-rw-r--r-- | Gemfile | 3 | ||||
-rw-r--r-- | INSTALL.md | 52 | ||||
-rw-r--r-- | TROUBLESHOOT.md | 46 | ||||
-rw-r--r-- | certs/app/controllers/certs_controller.rb | 11 | ||||
-rw-r--r-- | certs/app/models/client_certificate.rb | 18 | ||||
-rw-r--r-- | certs/test/functional/certs_controller_test.rb | 37 | ||||
-rw-r--r-- | certs/test/unit/client_certificate_test.rb | 23 | ||||
-rw-r--r-- | config/defaults.yml | 3 | ||||
-rw-r--r-- | core/test/support/with_config_helper.rb | 16 | ||||
-rw-r--r-- | help/app/models/ticket.rb | 2 | ||||
-rw-r--r-- | users/app/assets/javascripts/users.js.coffee | 4 | ||||
-rw-r--r-- | users/app/controllers/users_controller.rb | 8 | ||||
-rw-r--r-- | users/app/models/user.rb | 14 | ||||
-rw-r--r-- | users/app/views/users/_cancel_account.html.haml | 9 | ||||
-rw-r--r-- | users/app/views/users/_login_and_password_fields.html.haml | 2 | ||||
-rw-r--r-- | users/app/views/users/_password_fields.html.haml | 2 | ||||
-rw-r--r-- | users/app/views/users/edit.html.haml | 25 | ||||
-rw-r--r-- | users/config/locales/en.yml | 7 | ||||
-rw-r--r-- | users/test/functional/users_controller_test.rb | 13 |
19 files changed, 230 insertions, 65 deletions
@@ -13,7 +13,8 @@ gem 'leap_web_help', :path => 'help' # To use debugger gem 'debugger', :platforms => :mri_19 -gem 'ruby-debug', :platforms => :mri_18 +# ruby 1.8 is not supported anymore +# gem 'ruby-debug', :platforms => :mri_18 # unreleased so far ... but leap_web_certs need it @@ -1,5 +1,23 @@ # Installation # +Please see TROUBLESHOOT.md if you run into any issues during install. + +## TL;DR ## + +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 +cd leap_web +git submodule init +git submodule update +bundle install --binstubs +bin/rails server +``` + +You will find Leap Web running on `localhost:3000`. Check out the Cert Distribution section below for setting up the cert and server config. + ## Requirements ## The webapp only depends on very basic ruby packages and installs the other requirements as gems through bundler. @@ -9,17 +27,31 @@ The webapp only depends on very basic ruby packages and installs the other requi The following packages need to be installed: * git -* ruby (1.8.7 and 1.9.3 work) +* ruby1.9.3 * rubygems * couchdb +### Code ### + +Simply clone the git repository: + +``` + git clone git://github.com/leapcode/leap_web.git + cd leap_web +``` + ### Gems ### -We install most gems we depend upon through [bundler](http://gembundler.com). However the bundler gem needs to be installed and the `bundle` command needs to be available to the user used for deploy. +We install most gems we depend upon through [bundler](http://gembundler.com). First install bundler -### Bundler ### +``` + gem install bundler +``` -Run `bundle install` to install all the required gems. +Then install all the required gems: +``` + bundle install --binstubs +``` ## Setup ## @@ -32,13 +64,15 @@ We currently use a git submodule to include srp-js. This will soon be replaced b git submodule update ``` -### Cert Distribution ### - -The Webapp can hand out certs for the EIP client. These certs are either picked from a pool in CouchDB or from a file. For now you can either run [Leap CA](http://github.com/leapcode/leap_ca) to fill the pool or you can put your certs file in config/cert. +### Provider Information ### -We also ship provider information through the webapp. For now please add your eip-service.json to the public/config directory. +The leap client fetches provider information via json files from the server. +If you want to use that functionality please add your provider files the public/config directory. ## Running ## -Run `rails server`, `bundle exec rails server` or whatever rack server you prefer. +``` +bin/rails server +``` +You'll find Leap Web running on `localhost:3000` diff --git a/TROUBLESHOOT.md b/TROUBLESHOOT.md new file mode 100644 index 0000000..f3db006 --- /dev/null +++ b/TROUBLESHOOT.md @@ -0,0 +1,46 @@ +# Troubleshooting # + +Here are some less common issues you might run into when installing Leap Web. + +## Cannot find Bundler ## + +### Error Messages ### + +`bundle: command not found` + +### Solution ### + +Make sure bundler is installed. `gem list bundler` should list `bundler`. +You also need to be able to access the `bundler` executable in your PATH. + +## Outdated version of rubygems ## + +### Error Messages ### + +`bundler requires rubygems >= 1.3.6` + +### Solution ### + +`gem update --system` will install the latest rubygems + +## Missing development tools ## + +Some required gems will compile C extensions. They need a bunch of utils for this. + +### Error Messages ### + +`make: Command not found` + +### Solution ### + +Install the required tools. For linux the `build-essential` package provides most of them. For Mac OS you probably want the XCode Commandline tools. + +## Missing libraries and headers ## + +Some gem dependencies might not compile because they lack the needed c libraries. + +### Solution ### + +Install the libraries in question including their development files. + + diff --git a/certs/app/controllers/certs_controller.rb b/certs/app/controllers/certs_controller.rb index 6db270c..977e03e 100644 --- a/certs/app/controllers/certs_controller.rb +++ b/certs/app/controllers/certs_controller.rb @@ -1,11 +1,16 @@ class CertsController < ApplicationController - before_filter :authorize + before_filter :logged_in_or_free_certs # GET /cert def show - @cert = ClientCertificate.new - render :text => @cert.key + @cert.cert, :content_type => 'text/plain' + @cert = ClientCertificate.new(free: !logged_in?) + render text: @cert.to_s, content_type: 'text/plain' end + protected + + def logged_in_or_free_certs + authorize unless APP_CONFIG[:free_certs_enabled] + end end diff --git a/certs/app/models/client_certificate.rb b/certs/app/models/client_certificate.rb index be0ac63..13e0318 100644 --- a/certs/app/models/client_certificate.rb +++ b/certs/app/models/client_certificate.rb @@ -1,5 +1,5 @@ # -# Model for certificates stored in CouchDB. +# Model for certificates # # This file must be loaded after Config has been loaded. # @@ -17,11 +17,11 @@ class ClientCertificate # # generate the private key and client certificate # - def initialize + def initialize(options = {}) cert = CertificateAuthority::Certificate.new # set subject - cert.subject.common_name = random_common_name + cert.subject.common_name = common_name(options[:free]) # set expiration cert.not_before = yesterday @@ -35,8 +35,12 @@ class ClientCertificate cert.parent = ClientCertificate.root_ca cert.sign! client_signing_profile - self.key = cert.key_material.private_key.to_pem - self.cert = cert.to_pem + self.key = cert.key_material.private_key + self.cert = cert + end + + def to_s + self.key.to_pem + self.cert.to_pem end private @@ -61,6 +65,10 @@ class ClientCertificate Digest::MD5.hexdigest("#{rand(10**10)} -- #{Time.now}").to_i(16) end + def common_name(for_free_cert = false) + (for_free_cert ? APP_CONFIG[:free_cert_prefix] : '') + random_common_name + end + # # for the random common name, we need a text string that will be unique across all certs. # ruby 1.8 doesn't have a built-in uuid generator, or we would use SecureRandom.uuid diff --git a/certs/test/functional/certs_controller_test.rb b/certs/test/functional/certs_controller_test.rb index 75256ca..7826dd6 100644 --- a/certs/test/functional/certs_controller_test.rb +++ b/certs/test/functional/certs_controller_test.rb @@ -1,21 +1,40 @@ require 'test_helper' class CertsControllerTest < ActionController::TestCase - setup do - end - test "should require login" do + test "send free cert without login" do + cert = stub :to_s => "free cert" + ClientCertificate.expects(:new).with(free: true).returns(cert) get :show - assert_response :redirect - assert_redirected_to login_url + assert_response :success + assert_equal cert.to_s, @response.body end - test "should send cert" do + test "send cert" do login - cert = stub :cert => "adsf", :key => "key" - ClientCertificate.expects(:new).returns(cert) + cert = stub :to_s => "real cert" + ClientCertificate.expects(:new).with(free: false).returns(cert) get :show assert_response :success - assert_equal cert.key + cert.cert, @response.body + assert_equal cert.to_s, @response.body + end + + test "login required if free certs disabled" do + with_config free_certs_enabled: false do + get :show + assert_response :redirect + end end + + test "get paid cert if free certs disabled" do + with_config free_certs_enabled: false do + login + cert = stub :to_s => "real cert" + ClientCertificate.expects(:new).with(free: false).returns(cert) + get :show + assert_response :success + assert_equal cert.to_s, @response.body + end + end + end diff --git a/certs/test/unit/client_certificate_test.rb b/certs/test/unit/client_certificate_test.rb index 71a1d90..abb5560 100644 --- a/certs/test/unit/client_certificate_test.rb +++ b/certs/test/unit/client_certificate_test.rb @@ -2,17 +2,28 @@ require 'test_helper' class ClientCertificateTest < ActiveSupport::TestCase - setup do - @sample = ClientCertificate.new + test "new cert has all we need" do + sample = ClientCertificate.new + assert sample.key + assert sample.cert + assert sample.to_s end - test "new cert has all we need" do - assert @sample.key - assert @sample.cert + test "free cert has configured prefix" do + sample = ClientCertificate.new(free: true) + prefix = APP_CONFIG[:free_cert_prefix] + assert sample.cert.subject.common_name.starts_with?(prefix) + end + + test "real cert has no free cert prefix" do + sample = ClientCertificate.new + prefix = APP_CONFIG[:free_cert_prefix] + assert !sample.cert.subject.common_name.starts_with?(prefix) end test "cert issuer matches ca subject" do - cert = OpenSSL::X509::Certificate.new(@sample.cert) + sample = ClientCertificate.new + cert = OpenSSL::X509::Certificate.new(sample.cert.to_pem) assert_equal ClientCertificate.root_ca.openssl_body.subject, cert.issuer end diff --git a/config/defaults.yml b/config/defaults.yml index cb8a627..d0fb52f 100644 --- a/config/defaults.yml +++ b/config/defaults.yml @@ -7,6 +7,8 @@ cert_options: &cert_options client_cert_lifespan: 2 client_cert_bit_size: 2024 client_cert_hash: "SHA256" + free_certs_enabled: true + free_cert_prefix: "FREE" development: <<: *dev_ca @@ -19,7 +21,6 @@ test: <<: *cert_options admins: [admin, admin2] domain: test.me - production: <<: *cert_options diff --git a/core/test/support/with_config_helper.rb b/core/test/support/with_config_helper.rb new file mode 100644 index 0000000..65eb7bc --- /dev/null +++ b/core/test/support/with_config_helper.rb @@ -0,0 +1,16 @@ +module WithConfigHelper + extend ActiveSupport::Concern + + def with_config(options) + old_config = APP_CONFIG.dup + APP_CONFIG.merge! options + yield + ensure + APP_CONFIG.replace old_config + end + +end + +class ActiveSupport::TestCase + include WithConfigHelper +end diff --git a/help/app/models/ticket.rb b/help/app/models/ticket.rb index ed1ff9d..a456fe5 100644 --- a/help/app/models/ticket.rb +++ b/help/app/models/ticket.rb @@ -2,7 +2,7 @@ class Ticket < CouchRest::Model::Base #include ActiveModel::Validations use_database "tickets" - require 'securerandom' + #require 'securerandom' =begin title created_at diff --git a/users/app/assets/javascripts/users.js.coffee b/users/app/assets/javascripts/users.js.coffee index 86bacee..955556c 100644 --- a/users/app/assets/javascripts/users.js.coffee +++ b/users/app/assets/javascripts/users.js.coffee @@ -37,8 +37,8 @@ $(document).ready -> $('#new_user').submit srp.signup $('#new_session').submit preventDefault $('#new_session').submit srp.login - $('.user.form.change_password').submit srp.update - $('.user.form.change_password').submit preventDefault + $('.user.form.update_login_and_password').submit srp.update + $('.user.form.update_login_and_password').submit preventDefault $('.user.typeahead').typeahead({source: pollUsers}); $('a[data-toggle="tab"]').on('shown', -> $(ClientSideValidations.selectors.forms).validate() diff --git a/users/app/controllers/users_controller.rb b/users/app/controllers/users_controller.rb index 9325bc0..dff1ed5 100644 --- a/users/app/controllers/users_controller.rb +++ b/users/app/controllers/users_controller.rb @@ -1,7 +1,8 @@ class UsersController < ApplicationController - before_filter :authorize, :only => [:show, :edit, :update, :destroy] + before_filter :authorize, :only => [:show, :edit, :destroy, :update] before_filter :fetch_user, :only => [:show, :edit, :update, :destroy] + before_filter :authorize_self, :only => [:update] before_filter :set_anchor, :only => [:edit, :update] before_filter :authorize_admin, :only => [:index] @@ -57,6 +58,11 @@ class UsersController < ApplicationController access_denied unless admin? or (@user == current_user) end + def authorize_self + # have already checked that authorized + access_denied unless (@user == current_user) + end + def set_anchor @anchor = email_settings? ? :email : :account end diff --git a/users/app/models/user.rb b/users/app/models/user.rb index e41c2dc..c9b367f 100644 --- a/users/app/models/user.rb +++ b/users/app/models/user.rb @@ -18,9 +18,19 @@ class User < CouchRest::Model::Base :uniqueness => true, :if => :serverside? + # Have multiple regular expression validations so we can get specific error messages: validates :login, - :format => { :with => /\A[A-Za-z\d_\.]+\z/, - :message => "Only letters, digits, . and _ allowed" } + :format => { :with => /\A.{2,}\z/, + :message => "Login 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"} + validates :login, + :format => { :with => /\A.*[a-z\d]\z/, + :message => "Login must end with a letter or digit"} validate :login_is_unique_alias diff --git a/users/app/views/users/_cancel_account.html.haml b/users/app/views/users/_cancel_account.html.haml index 41580b0..756170b 100644 --- a/users/app/views/users/_cancel_account.html.haml +++ b/users/app/views/users/_cancel_account.html.haml @@ -1,6 +1,9 @@ %legend - =t :cancel_account - %small You will not be able to login anymore. + - if @user == current_user + =t :cancel_account + %small You will not be able to login anymore. + - else + =t :admin_cancel_account, :username => @user.login = link_to user_path(@user), :method => :delete, :class => "btn btn-danger" do %i.icon-remove.icon-white - Remove my Account + =t :remove_account diff --git a/users/app/views/users/_login_and_password_fields.html.haml b/users/app/views/users/_login_and_password_fields.html.haml new file mode 100644 index 0000000..0baefc7 --- /dev/null +++ b/users/app/views/users/_login_and_password_fields.html.haml @@ -0,0 +1,2 @@ += render :partial => 'login_field', :locals => {:f => f} += render :partial => 'password_fields', :locals => {:f => f, :password_confirmation_hint => t(:can_retype_old_password)}
\ No newline at end of file diff --git a/users/app/views/users/_password_fields.html.haml b/users/app/views/users/_password_fields.html.haml index c2e6a69..47b7b07 100644 --- a/users/app/views/users/_password_fields.html.haml +++ b/users/app/views/users/_password_fields.html.haml @@ -1,2 +1,2 @@ = f.input :password, :required => true, :validate => true, :input_html => { :id => :srp_password } -= f.input :password_confirmation, :required => true, :input_html => { :id => :srp_password_confirmation } += f.input :password_confirmation, :required => true, :hint => local_assigns[:password_confirmation_hint], :input_html => { :id => :srp_password_confirmation } diff --git a/users/app/views/users/edit.html.haml b/users/app/views/users/edit.html.haml index 950a3b1..97bd48d 100644 --- a/users/app/views/users/edit.html.haml +++ b/users/app/views/users/edit.html.haml @@ -1,14 +1,17 @@ .span8.offset2 %h2=t :settings + - tabs = [] - content_for :account do - = user_form_with 'login_field', :legend => :change_login - = user_form_with 'password_fields', :legend => :change_password - = render 'cancel_account' if @user == current_user - - content_for :email do - %legend=t :email_address - The associated email address is - = render @user.email_address, :as => :span - = user_form_with 'public_key_field', :legend => :public_key - = user_form_with 'email_forward_field', :legend => :forward_email - = user_form_with 'email_aliases', :legend => :add_email_alias - = render 'tabs/tabs', :tabs => [:account, :email] + = user_form_with 'login_and_password_fields', :legend => :update_login_and_password if @user == current_user + = render 'cancel_account' + - tabs << :account + - if @user == current_user + - content_for :email do + %legend=t :email_address + =t :associated_email + = render @user.email_address, :as => :span + = user_form_with 'public_key_field', :legend => :public_key + = user_form_with 'email_forward_field', :legend => :forward_email + = user_form_with 'email_aliases', :legend => :add_email_alias + - tabs << :email + = render 'tabs/tabs', :tabs => tabs diff --git a/users/config/locales/en.yml b/users/config/locales/en.yml index 493b5db..1b2789e 100644 --- a/users/config/locales/en.yml +++ b/users/config/locales/en.yml @@ -7,9 +7,10 @@ en: login_message: "Please login with your account." wrong_password: "wrong password" user_not_found: "could not be found" - change_login: "Change Login" - change_password: "Change Password" + update_login_and_password: "Update Login and Password" cancel_account: "Cancel your account" + remove_account: "Remove Account" + admin_cancel_account: "Cancel the account %{username}" set_email_address: "Set email address" forward_email: "Forward email" email_aliases: "Email aliases" @@ -19,6 +20,8 @@ en: user_created_successfully: "Successfully created your account." email_alias_destroyed_successfully: "Successfully removed the alias '%{alias}'." use_ascii_key: "Use ASCII-armored PGP key" + can_retype_old_password: "Retype your old password if you would like to keep that" + associated_email: "The associated email address is" activemodel: models: diff --git a/users/test/functional/users_controller_test.rb b/users/test/functional/users_controller_test.rb index 9fb06c9..fd8869a 100644 --- a/users/test/functional/users_controller_test.rb +++ b/users/test/functional/users_controller_test.rb @@ -130,20 +130,17 @@ class UsersControllerTest < ActionController::TestCase assert_equal " ", @response.body end - test "admin can update user" do + # Eventually, admin will be able to update some user fields + test "admin cannot update user" do user = find_record :user changed_attribs = record_attributes_for :user_with_settings - user.expects(:attributes=).with(changed_attribs.stringify_keys) - user.expects(:changed?).returns(true) - user.expects(:save).returns(true) - user.stubs(:email_aliases).returns([]) login :is_admin? => true put :update, :user => changed_attribs, :id => user.id, :format => :json - assert_equal user, assigns[:user] - assert_response 204 - assert_equal " ", @response.body + assert_response :redirect + assert_access_denied + end test "admin can destroy user" do |