leap_web_users 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +38 -0
- data/app/controllers/sessions_controller.rb +27 -0
- data/app/controllers/users_controller.rb +17 -0
- data/app/helpers/sessions_helper.rb +2 -0
- data/app/helpers/users_helper.rb +2 -0
- data/app/models/unauthenticated_user.rb +4 -0
- data/app/models/user.rb +49 -0
- data/app/views/sessions/new.html.haml +7 -0
- data/app/views/users/new.html.haml +10 -0
- data/config/initializers/error_constants.rb +1 -0
- data/config/routes.rb +10 -0
- data/lib/leap_web_users.rb +4 -0
- data/lib/leap_web_users/engine.rb +11 -0
- data/lib/leap_web_users/version.rb +3 -0
- data/lib/tasks/leap_web_users_tasks.rake +4 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +65 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +31 -0
- data/test/dummy/config/environments/production.rb +64 -0
- data/test/dummy/config/environments/test.rb +35 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/log/development.log +0 -0
- data/test/dummy/log/test.log +1145 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/functional/sessions_controller_test.rb +73 -0
- data/test/functional/users_controller_test.rb +33 -0
- data/test/integration/api/Readme.md +23 -0
- data/test/integration/api/account_flow_test.rb +69 -0
- data/test/integration/api/python/login_wrong_username.py +19 -0
- data/test/integration/api/python/signup.py +20 -0
- data/test/integration/api/python/signup_and_login.py +48 -0
- data/test/integration/api/python/signup_and_login_wrong_password.py +43 -0
- data/test/integration/navigation_test.rb +9 -0
- data/test/leap_web_users_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/helpers/session_helper_test.rb +4 -0
- data/test/unit/helpers/users_helper_test.rb +4 -0
- data/test/unit/unauthorized_user_test.rb +7 -0
- data/test/unit/user_test.rb +40 -0
- 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))
|