casino 4.0.3 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/stylesheets/casino.scss +60 -1
- data/app/controllers/casino/login_attempts_controller.rb +10 -0
- data/app/controllers/casino/sessions_controller.rb +3 -0
- data/app/helpers/casino/sessions_helper.rb +14 -0
- data/app/models/casino/login_attempt.rb +11 -0
- data/app/models/casino/model_concern/browser_info.rb +14 -0
- data/app/models/casino/ticket_granting_ticket.rb +1 -11
- data/app/models/casino/user.rb +1 -0
- data/app/views/casino/kaminari/_next_page.html.erb +5 -0
- data/app/views/casino/kaminari/_paginator.html.erb +6 -0
- data/app/views/casino/kaminari/_prev_page.html.erb +5 -0
- data/app/views/casino/login_attempts/_table.html.erb +28 -0
- data/app/views/casino/login_attempts/index.html.erb +14 -0
- data/app/views/casino/sessions/index.html.erb +12 -5
- data/casino.gemspec +1 -0
- data/config/locales/de.yml +34 -0
- data/config/locales/en.yml +35 -1
- data/config/locales/fr.yml +16 -0
- data/config/locales/pt-BR.yml +16 -0
- data/config/locales/ru.yml +110 -0
- data/config/locales/zh-CN.yml +16 -0
- data/config/locales/zh-TW.yml +16 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20160502074450_create_login_attempts.rb +14 -0
- data/lib/casino/engine.rb +1 -0
- data/lib/casino/version.rb +1 -1
- data/spec/controllers/login_attempts_controller_spec.rb +35 -0
- data/spec/controllers/sessions_controller_spec.rb +121 -68
- data/spec/dummy/db/schema.rb +11 -2
- data/spec/features/login_attempts_spec.rb +24 -0
- data/spec/features/session_overview_spec.rb +12 -0
- data/spec/model/login_attempt_spec.rb +7 -0
- data/spec/model/ticket_granting_ticket_spec.rb +5 -29
- data/spec/support/factories/login_attempt_factory.rb +10 -0
- data/spec/support/has_browser_info.rb +29 -0
- data/spec/support/kaminari.rb +3 -0
- metadata +38 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51eff1f39a70f787e2addb40bef646e6d3c5d280
|
|
4
|
+
data.tar.gz: 79bff8cc8bd3f72da3474ff258b6f85c8ad0b4be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb885c575ed4c09c1aeb767994e05b6859c4a0682dbb5b3a84ae4a3479271b2447faaaf5cdb9641b7d5e1eed763e1d8e1af881c7ecb5bee0c90bc340cf60de21
|
|
7
|
+
data.tar.gz: a4199bd96c19138bc07230598fcd80c36b4b2c2b3f4c9ae3e5d1a8fcc4a4eab4a3dbe9b02bcf03dfc2beab8212b7fa432ff85f4c7e1e5c610e777c69d2e97c5e
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
CASino Rails Engine (used in CASinoApp).
|
|
4
4
|
|
|
5
|
-
It currently supports [CAS 1.0 and CAS 2.0](http://
|
|
5
|
+
It currently supports [CAS 1.0 and CAS 2.0](http://apereo.github.io/cas) as well as [CAS 3.1 Single Sign Out](https://wiki.jasig.org/display/CASUM/Single+Sign+Out).
|
|
6
6
|
|
|
7
7
|
## Setup
|
|
8
8
|
|
|
@@ -301,6 +301,35 @@ table {
|
|
|
301
301
|
table.tickets {
|
|
302
302
|
margin-bottom: 10px;
|
|
303
303
|
}
|
|
304
|
+
|
|
305
|
+
table.login_attempts {
|
|
306
|
+
margin-bottom: 10px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
a.all_login_attempts {
|
|
310
|
+
font-size: .95em;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/// LOGIN ATTEMPTS ///
|
|
315
|
+
.login_attempts_index {
|
|
316
|
+
width: 800px;
|
|
317
|
+
|
|
318
|
+
h1 {
|
|
319
|
+
float: left;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.button {
|
|
323
|
+
float: right;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.pagination {
|
|
327
|
+
padding-top: .6em;
|
|
328
|
+
height: 2em;
|
|
329
|
+
.next {
|
|
330
|
+
float: right;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
304
333
|
}
|
|
305
334
|
|
|
306
335
|
/// LOGOUT ///
|
|
@@ -319,6 +348,24 @@ table {
|
|
|
319
348
|
}
|
|
320
349
|
|
|
321
350
|
|
|
351
|
+
@media only screen and (min-width: 800px) {
|
|
352
|
+
th.browser_info {
|
|
353
|
+
width: 16em;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
th.created_at, th.activity {
|
|
357
|
+
width: 16em;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
th.actions, th.successful {
|
|
361
|
+
width: 10em;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
td.successful {
|
|
365
|
+
padding-left: 2em;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
322
369
|
/// RESPONSIVE ///
|
|
323
370
|
@media only screen and (max-width: 600px) {
|
|
324
371
|
.container {
|
|
@@ -329,6 +376,12 @@ table {
|
|
|
329
376
|
width: 100%;
|
|
330
377
|
}
|
|
331
378
|
|
|
379
|
+
.login_attempts {
|
|
380
|
+
table {
|
|
381
|
+
padding-top: 100px;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
332
385
|
.sessions, .logout {
|
|
333
386
|
.info {
|
|
334
387
|
margin-top: 40px;
|
|
@@ -366,11 +419,17 @@ table {
|
|
|
366
419
|
|
|
367
420
|
@media only screen and (max-width: 800px) {
|
|
368
421
|
.container {
|
|
369
|
-
.sessions {
|
|
422
|
+
.sessions, .login_attempts_index {
|
|
370
423
|
margin-top: 10px;
|
|
371
424
|
width: 100%;
|
|
372
425
|
}
|
|
373
426
|
|
|
427
|
+
.login_attempts_index {
|
|
428
|
+
table {
|
|
429
|
+
padding-top: 100px;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
374
433
|
table, thead, tbody, th, td, tr {
|
|
375
434
|
display: block;
|
|
376
435
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class CASino::LoginAttemptsController < CASino::ApplicationController
|
|
2
|
+
include CASino::SessionsHelper
|
|
3
|
+
|
|
4
|
+
before_action :ensure_signed_in, only: [:index]
|
|
5
|
+
|
|
6
|
+
def index
|
|
7
|
+
@login_attempts = current_user.login_attempts.order(created_at: :desc)
|
|
8
|
+
.page(params[:page]).per(10)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -11,6 +11,7 @@ class CASino::SessionsController < CASino::ApplicationController
|
|
|
11
11
|
def index
|
|
12
12
|
@ticket_granting_tickets = current_user.ticket_granting_tickets.active
|
|
13
13
|
@two_factor_authenticators = current_user.two_factor_authenticators.active
|
|
14
|
+
@login_attempts = current_user.login_attempts.order(created_at: :desc).first(5)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def new
|
|
@@ -22,6 +23,7 @@ class CASino::SessionsController < CASino::ApplicationController
|
|
|
22
23
|
def create
|
|
23
24
|
validation_result = validate_login_credentials(params[:username], params[:password])
|
|
24
25
|
if !validation_result
|
|
26
|
+
log_failed_login params[:username]
|
|
25
27
|
show_login_error I18n.t('login_credential_acceptor.invalid_login_credentials')
|
|
26
28
|
else
|
|
27
29
|
sign_in(validation_result, long_term: params[:rememberMe], credentials_supplied: true)
|
|
@@ -59,6 +61,7 @@ class CASino::SessionsController < CASino::ApplicationController
|
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
private
|
|
64
|
+
|
|
62
65
|
def show_login_error(message)
|
|
63
66
|
flash.now[:error] = message
|
|
64
67
|
render :new, status: :forbidden
|
|
@@ -33,6 +33,7 @@ module CASino::SessionsHelper
|
|
|
33
33
|
|
|
34
34
|
def sign_in(authentication_result, options = {})
|
|
35
35
|
tgt = acquire_ticket_granting_ticket(authentication_result, request.user_agent, request.remote_ip, options)
|
|
36
|
+
create_login_attempt(tgt.user, true)
|
|
36
37
|
set_tgt_cookie(tgt)
|
|
37
38
|
handle_signed_in(tgt, options)
|
|
38
39
|
end
|
|
@@ -50,7 +51,20 @@ module CASino::SessionsHelper
|
|
|
50
51
|
cookies.delete :tgt
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
def log_failed_login(username)
|
|
55
|
+
CASino::User.where(username: username).each do |user|
|
|
56
|
+
create_login_attempt(user, false)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def create_login_attempt(user, successful)
|
|
61
|
+
user.login_attempts.create! successful: successful,
|
|
62
|
+
user_ip: request.ip,
|
|
63
|
+
user_agent: request.user_agent
|
|
64
|
+
end
|
|
65
|
+
|
|
53
66
|
private
|
|
67
|
+
|
|
54
68
|
def handle_signed_in(tgt, options = {})
|
|
55
69
|
if tgt.awaiting_two_factor_authentication?
|
|
56
70
|
@ticket_granting_ticket = tgt
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module CASino::ModelConcern::BrowserInfo
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
def browser_info
|
|
5
|
+
unless self.user_agent.blank?
|
|
6
|
+
user_agent = UserAgent.parse(self.user_agent)
|
|
7
|
+
if user_agent.platform.nil?
|
|
8
|
+
"#{user_agent.browser}"
|
|
9
|
+
else
|
|
10
|
+
"#{user_agent.browser} (#{user_agent.platform})"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -2,6 +2,7 @@ require 'user_agent'
|
|
|
2
2
|
|
|
3
3
|
class CASino::TicketGrantingTicket < ActiveRecord::Base
|
|
4
4
|
include CASino::ModelConcern::Ticket
|
|
5
|
+
include CASino::ModelConcern::BrowserInfo
|
|
5
6
|
|
|
6
7
|
self.ticket_prefix = 'TGC'.freeze
|
|
7
8
|
|
|
@@ -28,17 +29,6 @@ class CASino::TicketGrantingTicket < ActiveRecord::Base
|
|
|
28
29
|
tgts.destroy_all
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def browser_info
|
|
32
|
-
unless self.user_agent.blank?
|
|
33
|
-
user_agent = UserAgent.parse(self.user_agent)
|
|
34
|
-
if user_agent.platform.nil?
|
|
35
|
-
"#{user_agent.browser}"
|
|
36
|
-
else
|
|
37
|
-
"#{user_agent.browser} (#{user_agent.platform})"
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
32
|
def same_user?(other_ticket)
|
|
43
33
|
if other_ticket.nil?
|
|
44
34
|
false
|
data/app/models/casino/user.rb
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<table width="100%" class="login_attempts">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th class="browser_info"><%= CASino::LoginAttempt.human_attribute_name(:browser_info) %></th>
|
|
5
|
+
<th class="user_ip"><%= CASino::LoginAttempt.human_attribute_name(:user_ip) %></th>
|
|
6
|
+
<th class="created_at"><%= CASino::LoginAttempt.human_attribute_name(:created_at) %></th>
|
|
7
|
+
<th class="successful"><%= CASino::LoginAttempt.human_attribute_name(:successful) %></th>
|
|
8
|
+
</tr>
|
|
9
|
+
</thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
<% @login_attempts.each do |login_attempt| %>
|
|
12
|
+
<tr>
|
|
13
|
+
<td data-label="<%= CASino::LoginAttempt.human_attribute_name(:browser_info) %>">
|
|
14
|
+
<%= login_attempt.browser_info %>
|
|
15
|
+
</td>
|
|
16
|
+
<td data-label="<%= CASino::LoginAttempt.human_attribute_name(:user_ip) %>">
|
|
17
|
+
<%= login_attempt.user_ip %>
|
|
18
|
+
</td>
|
|
19
|
+
<td data-label="<%= CASino::LoginAttempt.human_attribute_name(:created_at) %>">
|
|
20
|
+
<%= l login_attempt.created_at %>
|
|
21
|
+
</td>
|
|
22
|
+
<td class="successful" data-label="<%= CASino::LoginAttempt.human_attribute_name(:successful) %>">
|
|
23
|
+
<%= (login_attempt.successful ? '✅' : '❌').html_safe %>
|
|
24
|
+
</td>
|
|
25
|
+
</tr>
|
|
26
|
+
<% end %>
|
|
27
|
+
</tbody>
|
|
28
|
+
</table>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<div class="container">
|
|
2
|
+
<div class="login_attempts_index box">
|
|
3
|
+
<div class="info">
|
|
4
|
+
<h1><%= CASino::LoginAttempt.model_name.human(count: 2) %></h1>
|
|
5
|
+
<%= link_to t('shared.back'), sessions_path, :class => 'button' %>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<%= render 'table' %>
|
|
9
|
+
|
|
10
|
+
<%= paginate @login_attempts, views_prefix: 'casino' %>
|
|
11
|
+
<%= page_entries_info @login_attempts, entry_name: '' %>
|
|
12
|
+
</div>
|
|
13
|
+
<%= render 'footer' %>
|
|
14
|
+
</div>
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
<table width="100%" class="tickets">
|
|
26
26
|
<thead>
|
|
27
27
|
<tr>
|
|
28
|
-
<th><%= t('sessions.table.column_browser') %></th>
|
|
29
|
-
<th><%= t('sessions.table.column_services') %></th>
|
|
30
|
-
<th><%= t('sessions.table.column_activity') %></th>
|
|
31
|
-
<th
|
|
28
|
+
<th class="browser_info"><%= t('sessions.table.column_browser') %></th>
|
|
29
|
+
<th class="services"><%= t('sessions.table.column_services') %></th>
|
|
30
|
+
<th class="activity"><%= t('sessions.table.column_activity') %></th>
|
|
31
|
+
<th class="actions"> </th>
|
|
32
32
|
</tr>
|
|
33
33
|
</thead>
|
|
34
34
|
<tbody>
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
<%= ticket_granting_ticket.service_tickets.size %>
|
|
44
44
|
</td>
|
|
45
45
|
<td data-label="<%= t('sessions.table.column_activity') %>">
|
|
46
|
-
<%=
|
|
46
|
+
<span title="<%= l ticket_granting_ticket.updated_at %>">
|
|
47
|
+
<%= t 'datetime.ago', datetime: distance_of_time_in_words_to_now(ticket_granting_ticket.updated_at) %>
|
|
48
|
+
</span>
|
|
47
49
|
</td>
|
|
48
50
|
<td>
|
|
49
51
|
<% if current_ticket_granting_ticket?(ticket_granting_ticket) %>
|
|
@@ -56,6 +58,11 @@
|
|
|
56
58
|
<% end %>
|
|
57
59
|
</tbody>
|
|
58
60
|
</table>
|
|
61
|
+
|
|
62
|
+
<h3><%= t('sessions.last_login_attempts') %></h3>
|
|
63
|
+
<%= render partial: 'casino/login_attempts/table' %>
|
|
64
|
+
|
|
65
|
+
<%= link_to t('sessions.all_login_attempts'), login_attempts_path, class: 'all_login_attempts' %>
|
|
59
66
|
</div>
|
|
60
67
|
<%= render 'footer' %>
|
|
61
68
|
</div>
|
data/casino.gemspec
CHANGED
data/config/locales/de.yml
CHANGED
|
@@ -32,6 +32,8 @@ de:
|
|
|
32
32
|
column_activity: "Letzte Aktivität"
|
|
33
33
|
current_session: "Aktive Session"
|
|
34
34
|
end_session: "Session beenden"
|
|
35
|
+
last_login_attempts: Letzte Loginversuche
|
|
36
|
+
all_login_attempts: Alle Loginversuche
|
|
35
37
|
two_factor_authenticators:
|
|
36
38
|
title: "Zwei-Faktor-Authentifizierung"
|
|
37
39
|
setup: "Zwei-Faktor-Authentifizierung einrichten"
|
|
@@ -86,3 +88,35 @@ de:
|
|
|
86
88
|
x_seconds:
|
|
87
89
|
one: eine Sekunde
|
|
88
90
|
other: ! '%{count} Sekunden'
|
|
91
|
+
time:
|
|
92
|
+
formats:
|
|
93
|
+
default: "%Y-%m-%d %H:%M"
|
|
94
|
+
shared:
|
|
95
|
+
back: Zurück
|
|
96
|
+
activerecord:
|
|
97
|
+
attributes:
|
|
98
|
+
casino/login_attempt:
|
|
99
|
+
created_at: Zeitpunkt
|
|
100
|
+
browser_info: Browser
|
|
101
|
+
user_ip: IP-Adresse
|
|
102
|
+
successful: Erfolgreich
|
|
103
|
+
models:
|
|
104
|
+
casino/login_attempt:
|
|
105
|
+
one: Loginversuch
|
|
106
|
+
other: Loginversuche
|
|
107
|
+
views:
|
|
108
|
+
pagination:
|
|
109
|
+
first: "« Erster"
|
|
110
|
+
last: Letzter »
|
|
111
|
+
next: Nächste Seite
|
|
112
|
+
previous: Vorherige Seite
|
|
113
|
+
truncate: "…"
|
|
114
|
+
helpers:
|
|
115
|
+
page_entries_info:
|
|
116
|
+
one_page:
|
|
117
|
+
display_entries:
|
|
118
|
+
one: Zeige <b>1</b> %{entry_name}
|
|
119
|
+
other: Zeige <b>alle %{count}</b> %{entry_name}
|
|
120
|
+
zero: Keine %{entry_name} gefunden
|
|
121
|
+
more_pages:
|
|
122
|
+
display_entries: Zeige %{entry_name} <b>%{first} - %{last}</b> von <b>%{total}</b>
|
data/config/locales/en.yml
CHANGED
|
@@ -32,6 +32,8 @@ en:
|
|
|
32
32
|
column_activity: "Most recent activity"
|
|
33
33
|
current_session: "Current session"
|
|
34
34
|
end_session: "End session"
|
|
35
|
+
last_login_attempts: Last Login Attempts
|
|
36
|
+
all_login_attempts: All Login Attempts
|
|
35
37
|
two_factor_authenticators:
|
|
36
38
|
title: "Two-factor authentication"
|
|
37
39
|
setup: "Set up two-factor authentication"
|
|
@@ -62,7 +64,7 @@ en:
|
|
|
62
64
|
one: about one year
|
|
63
65
|
other: about %{count} years
|
|
64
66
|
almost_x_years:
|
|
65
|
-
one: nearly one year
|
|
67
|
+
one: nearly one year
|
|
66
68
|
other: nearly %{count} years
|
|
67
69
|
half_a_minute: half minute
|
|
68
70
|
less_than_x_minutes:
|
|
@@ -86,3 +88,35 @@ en:
|
|
|
86
88
|
x_seconds:
|
|
87
89
|
one: one second
|
|
88
90
|
other: ! '%{count} seconds'
|
|
91
|
+
time:
|
|
92
|
+
formats:
|
|
93
|
+
default: "%Y-%m-%d %H:%M"
|
|
94
|
+
shared:
|
|
95
|
+
back: Back
|
|
96
|
+
activerecord:
|
|
97
|
+
attributes:
|
|
98
|
+
casino/login_attempt:
|
|
99
|
+
created_at: Time
|
|
100
|
+
browser_info: Browser
|
|
101
|
+
user_ip: IP Address
|
|
102
|
+
successful: Successful
|
|
103
|
+
models:
|
|
104
|
+
casino/login_attempt:
|
|
105
|
+
one: Login Attempt
|
|
106
|
+
other: Login Attempts
|
|
107
|
+
views:
|
|
108
|
+
pagination:
|
|
109
|
+
first: "« First"
|
|
110
|
+
last: "Last »"
|
|
111
|
+
previous: "‹ Prev"
|
|
112
|
+
next: "Next ›"
|
|
113
|
+
truncate: "…"
|
|
114
|
+
helpers:
|
|
115
|
+
page_entries_info:
|
|
116
|
+
one_page:
|
|
117
|
+
display_entries:
|
|
118
|
+
zero: "No %{entry_name} found"
|
|
119
|
+
one: "Displaying <b>1</b> %{entry_name}"
|
|
120
|
+
other: "Displaying <b>all %{count}</b> %{entry_name}"
|
|
121
|
+
more_pages:
|
|
122
|
+
display_entries: "Displaying %{entry_name} <b>%{first} - %{last}</b> of <b>%{total}</b> in total"
|