summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzul <azul@leap.se>2013-03-05 13:35:05 +0100
committerAzul <azul@leap.se>2013-03-05 13:35:05 +0100
commit27c16ccceffa1d8eaaf02612cf29a60bfe6ced01 (patch)
tree1df9d9900872cf2e97d5c27b4175816eff5cbf80
parent733426aa3992dafaf1c58ede7e74018057a01148 (diff)
parent87c306ea212c01ecc8f98009def5971fc4d5af11 (diff)
Merge branch 'master' into feature/limit_user_leak
Conflicts: users/lib/warden/strategies/secure_remote_password.rb
-rw-r--r--.gitignore1
-rw-r--r--Gemfile3
-rw-r--r--INSTALL.md52
-rw-r--r--TROUBLESHOOT.md46
-rw-r--r--certs/app/models/client_certificate.rb3
-rw-r--r--certs/test/unit/client_certificate_test.rb12
-rw-r--r--config/defaults.yml3
-rw-r--r--users/app/controllers/sessions_controller.rb2
-rw-r--r--users/app/controllers/v1/sessions_controller.rb10
-rw-r--r--users/app/models/user.rb14
-rw-r--r--users/app/views/sessions/_nav.html.haml2
-rw-r--r--users/app/views/v1/sessions/new.json.erb3
-rw-r--r--users/config/routes.rb9
-rw-r--r--users/leap_web_users.gemspec2
-rw-r--r--users/lib/warden/strategies/secure_remote_password.rb7
-rw-r--r--users/test/functional/sessions_controller_test.rb4
-rw-r--r--users/test/functional/v1/sessions_controller_test.rb68
-rwxr-xr-xusers/test/integration/api/python/flow_with_srp.py16
18 files changed, 215 insertions, 42 deletions
diff --git a/.gitignore b/.gitignore
index 3b31e0b..5536c6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ public/ca.crt
public/config/*
public/provider.json
config/config.yml
+bin
diff --git a/Gemfile b/Gemfile
index 4bf0f3b..56cbf62 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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
diff --git a/INSTALL.md b/INSTALL.md
index 9e93eb0..7e95b76 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -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/models/client_certificate.rb b/certs/app/models/client_certificate.rb
index 1bc34c6..13e0318 100644
--- a/certs/app/models/client_certificate.rb
+++ b/certs/app/models/client_certificate.rb
@@ -66,8 +66,7 @@ class ClientCertificate
end
def common_name(for_free_cert = false)
- random_common_name +
- (for_free_cert ? APP_CONFIG[:free_cert_postfix] : '')
+ (for_free_cert ? APP_CONFIG[:free_cert_prefix] : '') + random_common_name
end
#
diff --git a/certs/test/unit/client_certificate_test.rb b/certs/test/unit/client_certificate_test.rb
index bcc61cc..abb5560 100644
--- a/certs/test/unit/client_certificate_test.rb
+++ b/certs/test/unit/client_certificate_test.rb
@@ -9,16 +9,16 @@ class ClientCertificateTest < ActiveSupport::TestCase
assert sample.to_s
end
- test "free cert has configured postfix" do
+ test "free cert has configured prefix" do
sample = ClientCertificate.new(free: true)
- postfix = APP_CONFIG[:free_cert_postfix]
- assert sample.cert.subject.common_name.include?(postfix)
+ prefix = APP_CONFIG[:free_cert_prefix]
+ assert sample.cert.subject.common_name.starts_with?(prefix)
end
- test "real cert has no free cert postfix" do
+ test "real cert has no free cert prefix" do
sample = ClientCertificate.new
- postfix = APP_CONFIG[:free_cert_postfix]
- assert !sample.cert.subject.common_name.include?(postfix)
+ prefix = APP_CONFIG[:free_cert_prefix]
+ assert !sample.cert.subject.common_name.starts_with?(prefix)
end
test "cert issuer matches ca subject" do
diff --git a/config/defaults.yml b/config/defaults.yml
index 54e4178..d0fb52f 100644
--- a/config/defaults.yml
+++ b/config/defaults.yml
@@ -8,7 +8,7 @@ cert_options: &cert_options
client_cert_bit_size: 2024
client_cert_hash: "SHA256"
free_certs_enabled: true
- free_cert_postfix: "*Free Cert*"
+ free_cert_prefix: "FREE"
development:
<<: *dev_ca
@@ -21,7 +21,6 @@ test:
<<: *cert_options
admins: [admin, admin2]
domain: test.me
-
production:
<<: *cert_options
diff --git a/users/app/controllers/sessions_controller.rb b/users/app/controllers/sessions_controller.rb
index 0345fbd..01ecff6 100644
--- a/users/app/controllers/sessions_controller.rb
+++ b/users/app/controllers/sessions_controller.rb
@@ -1,7 +1,5 @@
class SessionsController < ApplicationController
- skip_before_filter :verify_authenticity_token
-
def new
@session = Session.new
if authentication_errors
diff --git a/users/app/controllers/v1/sessions_controller.rb b/users/app/controllers/v1/sessions_controller.rb
index 27d10fb..0551ca9 100644
--- a/users/app/controllers/v1/sessions_controller.rb
+++ b/users/app/controllers/v1/sessions_controller.rb
@@ -18,12 +18,20 @@ module V1
def update
authenticate!
- render :json => session.delete(:handshake)
+ render :json => login_response
end
def destroy
logout
redirect_to root_path
end
+
+ protected
+
+ def login_response
+ handshake = session.delete(:handshake)
+ handshake.to_hash.merge(:id => current_user.id)
+ end
+
end
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/sessions/_nav.html.haml b/users/app/views/sessions/_nav.html.haml
index 5306d0e..ac85bb5 100644
--- a/users/app/views/sessions/_nav.html.haml
+++ b/users/app/views/sessions/_nav.html.haml
@@ -5,7 +5,7 @@
%li
= link_to current_user.login, edit_user_path(current_user)
%li
- = link_to t(:logout), logout_path
+ = link_to t(:logout), logout_path, :method => :delete
- else
%li
= link_to t(:login), login_path
diff --git a/users/app/views/v1/sessions/new.json.erb b/users/app/views/v1/sessions/new.json.erb
new file mode 100644
index 0000000..36154b8
--- /dev/null
+++ b/users/app/views/v1/sessions/new.json.erb
@@ -0,0 +1,3 @@
+{
+"errors": <%= raw @errors.to_json %>
+}
diff --git a/users/config/routes.rb b/users/config/routes.rb
index 2cd1740..c50cb15 100644
--- a/users/config/routes.rb
+++ b/users/config/routes.rb
@@ -1,17 +1,18 @@
Rails.application.routes.draw do
constraints :subdomain => "api" do
- namespace "api", { module: "V1",
+ namespace "api", { module: "v1",
path: "/1/",
defaults: {format: 'json'} } do
- resources :sessions, :only => [:new, :create, :update, :destroy]
+ resources :sessions, :only => [:new, :create, :update]
+ delete "logout" => "sessions#destroy", :as => "logout"
resources :users, :only => [:create, :update]
end
end
get "login" => "sessions#new", :as => "login"
- get "logout" => "sessions#destroy", :as => "logout"
- resources :sessions, :only => [:new, :create, :update, :destroy]
+ delete "logout" => "sessions#destroy", :as => "logout"
+ resources :sessions, :only => [:new, :create, :update]
get "signup" => "users#new", :as => "signup"
resources :users do
diff --git a/users/leap_web_users.gemspec b/users/leap_web_users.gemspec
index 0182c1f..c57937f 100644
--- a/users/leap_web_users.gemspec
+++ b/users/leap_web_users.gemspec
@@ -17,6 +17,6 @@ Gem::Specification.new do |s|
s.add_dependency "leap_web_core", LeapWeb::VERSION
- s.add_dependency "ruby-srp", "~> 0.1.5"
+ s.add_dependency "ruby-srp", "~> 0.1.6"
s.add_dependency "rails_warden"
end
diff --git a/users/lib/warden/strategies/secure_remote_password.rb b/users/lib/warden/strategies/secure_remote_password.rb
index 5032914..f1b1a57 100644
--- a/users/lib/warden/strategies/secure_remote_password.rb
+++ b/users/lib/warden/strategies/secure_remote_password.rb
@@ -25,14 +25,17 @@ module Warden
end
def validate!
- client = session[:handshake].authenticate(params['client_auth'].hex)
- if client
+ if client = validate
success!(User.find_by_login(client.username))
else
fail!({:login => "invalid_user_pass", :password => "invalid_user_pass"})
end
end
+ def validate
+ session[:handshake].authenticate(params['client_auth'].hex)
+ end
+
def initialize!
if user = User.find_by_login(id)
client = SRP::Client.new user.username,
diff --git a/users/test/functional/sessions_controller_test.rb b/users/test/functional/sessions_controller_test.rb
index 9df4455..f99c0d7 100644
--- a/users/test/functional/sessions_controller_test.rb
+++ b/users/test/functional/sessions_controller_test.rb
@@ -47,10 +47,12 @@ class SessionsControllerTest < ActionController::TestCase
request.env['warden'].expects(:authenticate!)
handshake = stub(:to_json => "JSON")
session[:handshake] = handshake
+
post :update, :id => @user.login, :client_auth => @client_hex
+
assert_nil session[:handshake]
assert_response :success
- assert_equal handshake.to_json, @response.body
+ assert_json_response handshake
end
test "logout should reset warden user" do
diff --git a/users/test/functional/v1/sessions_controller_test.rb b/users/test/functional/v1/sessions_controller_test.rb
new file mode 100644
index 0000000..be085ce
--- /dev/null
+++ b/users/test/functional/v1/sessions_controller_test.rb
@@ -0,0 +1,68 @@
+require 'test_helper'
+
+# This is a simple controller unit test.
+# We're stubbing out both warden and srp.
+# There's an integration test testing the full rack stack and srp
+class V1::SessionsControllerTest < ActionController::TestCase
+
+ setup do
+ @request.env['HTTP_HOST'] = 'api.lvh.me'
+ @user = stub :login => "me", :id => 123
+ @client_hex = 'a123'
+ end
+
+ test "renders json" do
+ request.env['warden'].expects(:winning_strategy)
+ get :new, :format => :json
+ assert_response :success
+ assert_json_error nil
+ end
+
+ test "renders warden errors" do
+ strategy = stub :message => {:field => :translate_me}
+ request.env['warden'].stubs(:winning_strategy).returns(strategy)
+ I18n.expects(:t).with(:translate_me).at_least_once.returns("translation stub")
+ get :new, :format => :json
+ assert_response 422
+ assert_json_error :field => "translation stub"
+ end
+
+ # Warden takes care of parsing the params and
+ # rendering the response. So not much to test here.
+ test "should perform handshake" do
+ request.env['warden'].expects(:authenticate!)
+ # make sure we don't get a template missing error:
+ @controller.stubs(:render)
+ post :create, :login => @user.login, 'A' => @client_hex
+ end
+
+ test "should authorize" do
+ request.env['warden'].expects(:authenticate!)
+ @controller.expects(:current_user).returns(@user)
+ handshake = stub(:to_hash => {h: "ash"})
+ session[:handshake] = handshake
+
+ post :update, :id => @user.login, :client_auth => @client_hex
+
+ assert_nil session[:handshake]
+ assert_response :success
+ assert_json_response handshake.to_hash.merge(id: @user.id)
+ end
+
+ test "logout should reset warden user" do
+ expect_warden_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/integration/api/python/flow_with_srp.py b/users/test/integration/api/python/flow_with_srp.py
index df83dfb..7b741d6 100755
--- a/users/test/integration/api/python/flow_with_srp.py
+++ b/users/test/integration/api/python/flow_with_srp.py
@@ -12,11 +12,11 @@ import binascii
safe_unhexlify = lambda x: binascii.unhexlify(x) if (len(x) % 2 == 0) else binascii.unhexlify('0'+x)
# let's have some random name
-def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
+def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for x in range(size))
# using globals for a start
-server = 'http://api.lvh.me:3000/1'
+server = 'https://api.bitmask.net:4430/1'
login = id_generator()
password = id_generator() + id_generator()
@@ -25,9 +25,9 @@ password = id_generator() + id_generator()
# log the server communication
def print_and_parse(response):
- # print response.request.method + ': ' + response.url
- # print " " + json.dumps(response.request.data)
- # print " -> " + response.text
+ print response.request.method + ': ' + response.url
+ print " " + json.dumps(response.request.data)
+ print " -> " + response.text
return json.loads(response.text)
def signup(session):
@@ -39,7 +39,7 @@ def signup(session):
'user[password_verifier]': binascii.hexlify(vkey),
'user[password_salt]': binascii.hexlify(salt)
}
- return session.post(server + '/users.json', data = user_params)
+ return session.post(server + '/users.json', data = user_params, verify = False)
usr = srp.User( login, password, srp.SHA256, srp.NG_1024 )
@@ -50,12 +50,12 @@ def authenticate(session, login):
'login': uname,
'A': binascii.hexlify(A)
}
- init = print_and_parse(session.post(server + '/sessions', data = params))
+ init = print_and_parse(session.post(server + '/sessions', data = params, verify=False))
# print ' b = "' + init['b'] + '"'
# print ' bb = "' + init['B'] + '"'
M = usr.process_challenge( safe_unhexlify(init['salt']), safe_unhexlify(init['B']) )
# print ' m = "' + binascii.hexlify(M) + '"'
- return session.put(server + '/sessions/' + login,
+ return session.put(server + '/sessions/' + login, verify = False,
data = {'client_auth': binascii.hexlify(M)})
session = requests.session()