leap_web_users 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +38 -0
  4. data/app/controllers/sessions_controller.rb +27 -0
  5. data/app/controllers/users_controller.rb +17 -0
  6. data/app/helpers/sessions_helper.rb +2 -0
  7. data/app/helpers/users_helper.rb +2 -0
  8. data/app/models/unauthenticated_user.rb +4 -0
  9. data/app/models/user.rb +49 -0
  10. data/app/views/sessions/new.html.haml +7 -0
  11. data/app/views/users/new.html.haml +10 -0
  12. data/config/initializers/error_constants.rb +1 -0
  13. data/config/routes.rb +10 -0
  14. data/lib/leap_web_users.rb +4 -0
  15. data/lib/leap_web_users/engine.rb +11 -0
  16. data/lib/leap_web_users/version.rb +3 -0
  17. data/lib/tasks/leap_web_users_tasks.rake +4 -0
  18. data/test/dummy/README.rdoc +261 -0
  19. data/test/dummy/Rakefile +7 -0
  20. data/test/dummy/app/assets/javascripts/application.js +15 -0
  21. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  22. data/test/dummy/app/controllers/application_controller.rb +3 -0
  23. data/test/dummy/app/helpers/application_helper.rb +2 -0
  24. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +65 -0
  27. data/test/dummy/config/boot.rb +10 -0
  28. data/test/dummy/config/environment.rb +5 -0
  29. data/test/dummy/config/environments/development.rb +31 -0
  30. data/test/dummy/config/environments/production.rb +64 -0
  31. data/test/dummy/config/environments/test.rb +35 -0
  32. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  33. data/test/dummy/config/initializers/inflections.rb +15 -0
  34. data/test/dummy/config/initializers/mime_types.rb +5 -0
  35. data/test/dummy/config/initializers/secret_token.rb +7 -0
  36. data/test/dummy/config/initializers/session_store.rb +8 -0
  37. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  38. data/test/dummy/config/locales/en.yml +5 -0
  39. data/test/dummy/config/routes.rb +58 -0
  40. data/test/dummy/log/development.log +0 -0
  41. data/test/dummy/log/test.log +1145 -0
  42. data/test/dummy/public/404.html +26 -0
  43. data/test/dummy/public/422.html +26 -0
  44. data/test/dummy/public/500.html +25 -0
  45. data/test/dummy/public/favicon.ico +0 -0
  46. data/test/dummy/script/rails +6 -0
  47. data/test/functional/sessions_controller_test.rb +73 -0
  48. data/test/functional/users_controller_test.rb +33 -0
  49. data/test/integration/api/Readme.md +23 -0
  50. data/test/integration/api/account_flow_test.rb +69 -0
  51. data/test/integration/api/python/login_wrong_username.py +19 -0
  52. data/test/integration/api/python/signup.py +20 -0
  53. data/test/integration/api/python/signup_and_login.py +48 -0
  54. data/test/integration/api/python/signup_and_login_wrong_password.py +43 -0
  55. data/test/integration/navigation_test.rb +9 -0
  56. data/test/leap_web_users_test.rb +7 -0
  57. data/test/test_helper.rb +10 -0
  58. data/test/unit/helpers/session_helper_test.rb +4 -0
  59. data/test/unit/helpers/users_helper_test.rb +4 -0
  60. data/test/unit/unauthorized_user_test.rb +7 -0
  61. data/test/unit/user_test.rb +40 -0
  62. metadata +352 -0
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby1.8
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+
3
+ class SessionsControllerTest < ActionController::TestCase
4
+
5
+ def setup
6
+ @client_hex = 'a123'
7
+ @client_rnd = @client_hex.hex
8
+ @server_hex = 'b123'
9
+ @server_rnd = @server_hex.hex
10
+ @server_rnd_exp = 'e123'.hex
11
+ @server_handshake = stub :aa => @client_rnd, :bb => @server_rnd, :b => @server_rnd_exp
12
+ end
13
+
14
+ test "should get login screen" do
15
+ get :new
16
+ assert_response :success
17
+ end
18
+
19
+ test "should perform handshake" do
20
+ user = stub :login => "me", :id => 123
21
+ user.expects(:initialize_auth).
22
+ with(@client_rnd).
23
+ returns(@server_handshake)
24
+ User.expects(:find_by_param).with(user.login).returns(user)
25
+ post :create, :login => user.login, 'A' => @client_hex
26
+ assert_equal @server_handshake, session[:handshake]
27
+ assert_response :success
28
+ assert_json_response :B => @server_hex
29
+ end
30
+
31
+ test "should report user not found" do
32
+ unknown = "login_that_does_not_exist"
33
+ User.expects(:find_by_param).with(unknown).raises(RECORD_NOT_FOUND)
34
+ post :create, :login => unknown
35
+ assert_response :success
36
+ assert_json_response :errors => {"login" => ["unknown user"]}
37
+ end
38
+
39
+ test "should authorize" do
40
+ session[:handshake] = @server_handshake
41
+ user = stub :login => "me", :id => 123
42
+ user.expects(:authenticate!).
43
+ with(@client_rnd, @server_handshake).
44
+ returns(@server_auth)
45
+ User.expects(:find_by_param).with(user.login).returns(user)
46
+ post :update, :id => user.login, :client_auth => @client_hex
47
+ assert_nil session[:handshake]
48
+ assert_json_response :M2 => @server_auth
49
+ assert_equal user.id, session[:user_id]
50
+ end
51
+
52
+ test "should report wrong password" do
53
+ session[:handshake] = @server_handshake
54
+ user = stub :login => "me", :id => 123
55
+ user.expects(:authenticate!).
56
+ with(@client_rnd, @server_handshake).
57
+ raises(WRONG_PASSWORD)
58
+ User.expects(:find_by_param).with(user.login).returns(user)
59
+ post :update, :id => user.login, :client_auth => @client_hex
60
+ assert_nil session[:handshake]
61
+ assert_nil session[:user_id]
62
+ assert_json_response :errors => {"password" => ["wrong password"]}
63
+ end
64
+
65
+ test "logout should reset sessions user_id" do
66
+ session[:user_id] = "set"
67
+ delete :destroy
68
+ assert_nil session[:user_id]
69
+ assert_response :redirect
70
+ assert_redirected_to root_url
71
+ end
72
+
73
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ class UsersControllerTest < ActionController::TestCase
4
+ test "should get new" do
5
+ get :new
6
+ assert_equal User, assigns(:user).class
7
+ assert_response :success
8
+ end
9
+
10
+ test "should create new user" do
11
+ params = User.valid_attributes_hash
12
+ user = stub params.merge(:id => 123)
13
+ params.stringify_keys!
14
+ User.expects(:create!).with(params).returns(user)
15
+ post :create, :user => params
16
+ assert_nil session[:user_id]
17
+ assert_response :redirect
18
+ assert_redirected_to root_url
19
+ end
20
+
21
+ test "should redirect to signup form on failed attempt" do
22
+ params = User.valid_attributes_hash.slice(:login)
23
+ user = User.new(params)
24
+ params.stringify_keys!
25
+ User.expects(:create!).with(params).raises(VALIDATION_FAILED.new(user))
26
+ post :create, :user => params
27
+ assert_nil session[:user_id]
28
+ assert_equal user, assigns[:user]
29
+ assert_response :redirect
30
+ assert_redirected_to new_user_path
31
+ end
32
+
33
+ end
@@ -0,0 +1,23 @@
1
+ API tests
2
+ ==========
3
+
4
+
5
+ Testing the restful api from a simple python client as that's what we'll be using.
6
+
7
+ This test so far mostly demoes the API. We have no SRP calc in there.
8
+
9
+ TODO: keep track of the cookies during login. The server uses the session to keep track of the random numbers A and B.
10
+
11
+ The output of signup_and_login_wrong_password pretty well describes the SRP API:
12
+
13
+ ```
14
+ POST: http://localhost:9292/users.json
15
+ {"user[password_salt]": "54321", "user[password_verifier]": "12345", "user[login]": "SWQ055"}
16
+ -> {"password_salt":"54321","login":"SWQ055"}
17
+ POST: http://localhost:9292/sessions
18
+ {"A": "12345", "login": "SWQ055"}
19
+ -> {"B":"1778367531e93a4c7713c76f67649f35a4211ebc520926ae8c3848cd66171651"}
20
+ PUT: http://localhost:9292/sessions/SWQ055
21
+ {"M": "123ABC"}
22
+ -> {"field":"password","error":"wrong password"}
23
+ ```
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+
3
+ class AccountFlowTest < ActionDispatch::IntegrationTest
4
+
5
+ # this test wraps the api and implements the interface the ruby-srp client.
6
+ def handshake(login, aa)
7
+ post "sessions", :login => login, 'A' => aa.to_s(16)
8
+ assert_response :success
9
+ response = JSON.parse(@response.body)
10
+ if response['errors']
11
+ raise RECORD_NOT_FOUND.new(response['errors'])
12
+ else
13
+ return response['B'].hex
14
+ end
15
+ end
16
+
17
+ def validate(m)
18
+ put "sessions/" + @login, :client_auth => m.to_s(16)
19
+ assert_response :success
20
+ return JSON.parse(@response.body)
21
+ end
22
+
23
+ def setup
24
+ @login = "integration_test_user"
25
+ User.find_by_login(@login).tap{|u| u.destroy if u}
26
+ @password = "srp, verify me!"
27
+ @srp = SRP::Client.new(@login, @password)
28
+ @user_params = {
29
+ :login => @login,
30
+ :password_verifier => @srp.verifier.to_s(16),
31
+ :password_salt => @srp.salt.to_s(16)
32
+ }
33
+ end
34
+
35
+ def teardown
36
+ @user.destroy if @user # make sure we can run this test again
37
+ end
38
+
39
+ test "signup and login with srp via api" do
40
+ post '/users.json', :user => @user_params
41
+ @user = User.find_by_param(@login)
42
+ assert_json_response @user_params.slice(:login, :password_salt)
43
+ assert_response :success
44
+ server_auth = @srp.authenticate(self, @login, @password)
45
+ assert_nil server_auth["errors"]
46
+ assert server_auth["M2"]
47
+ end
48
+
49
+ test "signup and wrong password login attempt" do
50
+ post '/users.json', :user => @user_params
51
+ @user = User.find_by_param(@login)
52
+ assert_json_response @user_params.slice(:login, :password_salt)
53
+ assert_response :success
54
+ server_auth = @srp.authenticate(self, @login, "wrong password")
55
+ assert_equal ["wrong password"], server_auth["errors"]['password']
56
+ assert_nil server_auth["M2"]
57
+ end
58
+
59
+ test "signup and wrong username login attempt" do
60
+ post '/users.json', :user => @user_params
61
+ @user = User.find_by_param(@login)
62
+ assert_json_response @user_params.slice(:login, :password_salt)
63
+ assert_response :success
64
+ assert_raises RECORD_NOT_FOUND do
65
+ server_auth = @srp.authenticate(self, "wronglogin", @password)
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python
2
+
3
+ server = 'http://localhost:9292'
4
+
5
+ import requests
6
+ import json
7
+ import string
8
+ import random
9
+
10
+ def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
11
+ return ''.join(random.choice(chars) for x in range(size))
12
+
13
+ params = {
14
+ 'login': 'python_test_user_'+id_generator(),
15
+ 'A': '12345',
16
+ }
17
+ r = requests.post(server + '/sessions', data = params)
18
+ print r.url
19
+ print r.text
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python
2
+
3
+ server = 'http://localhost:9292'
4
+
5
+ import requests
6
+ import json
7
+ import string
8
+ import random
9
+
10
+ def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
11
+ return ''.join(random.choice(chars) for x in range(size))
12
+
13
+ user_params = {
14
+ 'user[login]': 'python_test_user_'+id_generator(),
15
+ 'user[password_verifier]': '12345',
16
+ 'user[password_salt]': '54321'
17
+ }
18
+ r = requests.post(server + '/users.json', data = user_params)
19
+ print r.url
20
+ print r.text
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python
2
+
3
+ # FAILS
4
+ #
5
+ # This test is currently failing for me because the session is not kept.
6
+ # Played with it a bunch - is probably messed up right now as well.
7
+
8
+
9
+ server = 'http://localhost:3000'
10
+
11
+ import requests
12
+ import json
13
+ import string
14
+ import random
15
+
16
+ def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
17
+ return ''.join(random.choice(chars) for x in range(size))
18
+
19
+ def print_and_parse(response):
20
+ print response.request.method + ': ' + response.url
21
+ print " " + json.dumps(response.request.data)
22
+ print " -> " + response.text
23
+ print " () " + json.dumps(requests.utils.dict_from_cookiejar(response.cookies))
24
+ return json.loads(response.text)
25
+
26
+ def signup(session):
27
+ user_params = {
28
+ 'user[login]': id_generator(),
29
+ 'user[password_verifier]': '12345',
30
+ 'user[password_salt]': '54321'
31
+ }
32
+ return session.post(server + '/users.json', data = user_params)
33
+
34
+ def authenticate(session, login):
35
+ params = {
36
+ 'login': login,
37
+ 'A': '12345',
38
+ }
39
+ init = session.post(server + '/sessions', data = params)
40
+ cookies = requests.utils.dict_from_cookiejar(init.cookies)
41
+ init = session.post(server + '/sessions', data = params, cookies = cookies)
42
+ print "(%) " + json.dumps(cookies)
43
+ return session.put(server + '/sessions/' + login, data = {'M': '123'}, cookies = cookies)
44
+
45
+ session = requests.session()
46
+ user = print_and_parse(signup(session))
47
+ # SRP signup would happen here and calculate M hex
48
+ auth = print_and_parse(authenticate(session, user['login']))
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python
2
+
3
+ server = 'http://localhost:9292'
4
+
5
+ import requests
6
+ import json
7
+ import string
8
+ import random
9
+
10
+ def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
11
+ return ''.join(random.choice(chars) for x in range(size))
12
+
13
+ def print_and_parse(response):
14
+ print response.request.method + ': ' + response.url
15
+ print " " + json.dumps(response.request.data)
16
+ print " -> " + response.text
17
+ # print " () " + json.dumps(requests.utils.dict_from_cookiejar(response.cookies))
18
+ return json.loads(response.text)
19
+
20
+ def signup():
21
+ user_params = {
22
+ 'user[login]': id_generator(),
23
+ 'user[password_verifier]': '12345',
24
+ 'user[password_salt]': '54321'
25
+ }
26
+ return requests.post(server + '/users.json', data = user_params)
27
+
28
+ def handshake(login):
29
+ params = {
30
+ 'login': login,
31
+ 'A': '12345',
32
+ }
33
+ return requests.post(server + '/sessions', data = params)
34
+
35
+ def authenticate(login, M):
36
+ return requests.put(server + '/sessions/' + login, data = {'M': M})
37
+
38
+
39
+ user = print_and_parse(signup())
40
+ handshake = print_and_parse(handshake(user['login']))
41
+ # SRP signup would happen here and calculate M hex
42
+ M = '123ABC'
43
+ auth = print_and_parse(authenticate(user['login'], M))
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ class NavigationTest < ActionDispatch::IntegrationTest
4
+
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class LeapWebUsersTest < ActiveSupport::TestCase
4
+ test "module exists" do
5
+ assert_kind_of Module, LeapWebUsers
6
+ end
7
+ end