Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
411829fd
Commit
411829fd
authored
Jul 03, 2015
by
Valery Sizov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Audit log for user authentication
parent
8ba83cba
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
144 additions
and
33 deletions
+144
-33
CHANGELOG
CHANGELOG
+2
-1
app/controllers/omniauth_callbacks_controller.rb
app/controllers/omniauth_callbacks_controller.rb
+8
-0
app/controllers/profiles_controller.rb
app/controllers/profiles_controller.rb
+5
-2
app/controllers/sessions_controller.rb
app/controllers/sessions_controller.rb
+7
-0
app/models/audit_event.rb
app/models/audit_event.rb
+19
-0
app/models/security_event.rb
app/models/security_event.rb
+2
-0
app/services/audit_event_service.rb
app/services/audit_event_service.rb
+25
-0
app/views/layouts/nav/_profile.html.haml
app/views/layouts/nav/_profile.html.haml
+3
-3
app/views/profiles/_event_table.html.haml
app/views/profiles/_event_table.html.haml
+16
-0
app/views/profiles/audit_log.html.haml
app/views/profiles/audit_log.html.haml
+5
-0
app/views/profiles/history.html.haml
app/views/profiles/history.html.haml
+0
-11
config/routes.rb
config/routes.rb
+1
-1
db/migrate/20141118150935_add_audit_event.rb
db/migrate/20141118150935_add_audit_event.rb
+22
-0
db/schema.rb
db/schema.rb
+16
-2
features/profile/active_tab.feature
features/profile/active_tab.feature
+3
-3
features/profile/profile.feature
features/profile/profile.feature
+1
-1
features/steps/profile/active_tab.rb
features/steps/profile/active_tab.rb
+2
-2
features/steps/profile/profile.rb
features/steps/profile/profile.rb
+1
-1
features/steps/shared/paths.rb
features/steps/shared/paths.rb
+2
-2
spec/features/security/profile_access_spec.rb
spec/features/security/profile_access_spec.rb
+2
-2
spec/routing/routing_spec.rb
spec/routing/routing_spec.rb
+2
-2
No files found.
CHANGELOG
View file @
411829fd
...
@@ -29,6 +29,7 @@ v 7.13.0 (unreleased)
...
@@ -29,6 +29,7 @@ v 7.13.0 (unreleased)
- Reporter role can manage issue tracker now: edit any issue, set assignee or milestone and manage labels
- Reporter role can manage issue tracker now: edit any issue, set assignee or milestone and manage labels
- Better performance for pages with events list, issues list and commits list
- Better performance for pages with events list, issues list and commits list
- Faster automerge check and merge itself when source and target branches are in same repository
- Faster automerge check and merge itself when source and target branches are in same repository
- Audit log for user authentication
v 7.12.1
v 7.12.1
- Fix error when deleting a user who has projects (Stan Hu)
- Fix error when deleting a user who has projects (Stan Hu)
...
...
app/controllers/omniauth_callbacks_controller.rb
View file @
411829fd
...
@@ -28,6 +28,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
...
@@ -28,6 +28,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Do additional LDAP checks for the user filter and EE features
# Do additional LDAP checks for the user filter and EE features
if
@user
.
allowed?
if
@user
.
allowed?
log_audit_event
(
gl_user
,
with: :ldap
)
sign_in_and_redirect
(
gl_user
)
sign_in_and_redirect
(
gl_user
)
else
else
flash
[
:alert
]
=
"Access denied for your LDAP account."
flash
[
:alert
]
=
"Access denied for your LDAP account."
...
@@ -47,6 +48,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
...
@@ -47,6 +48,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
if
current_user
if
current_user
# Add new authentication method
# Add new authentication method
current_user
.
identities
.
find_or_create_by
(
extern_uid:
oauth
[
'uid'
],
provider:
oauth
[
'provider'
])
current_user
.
identities
.
find_or_create_by
(
extern_uid:
oauth
[
'uid'
],
provider:
oauth
[
'provider'
])
log_audit_event
(
current_user
,
with:
oauth
[
'provider'
])
redirect_to
profile_account_path
,
notice:
'Authentication method updated'
redirect_to
profile_account_path
,
notice:
'Authentication method updated'
else
else
@user
=
Gitlab
::
OAuth
::
User
.
new
(
oauth
)
@user
=
Gitlab
::
OAuth
::
User
.
new
(
oauth
)
...
@@ -54,6 +56,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
...
@@ -54,6 +56,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login.
# Only allow properly saved users to login.
if
@user
.
persisted?
&&
@user
.
valid?
if
@user
.
persisted?
&&
@user
.
valid?
log_audit_event
(
@user
.
gl_user
,
with:
oauth
[
'provider'
])
sign_in_and_redirect
(
@user
.
gl_user
)
sign_in_and_redirect
(
@user
.
gl_user
)
else
else
error_message
=
error_message
=
...
@@ -83,4 +86,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
...
@@ -83,4 +86,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def
oauth
def
oauth
@oauth
||=
request
.
env
[
'omniauth.auth'
]
@oauth
||=
request
.
env
[
'omniauth.auth'
]
end
end
def
log_audit_event
(
user
,
options
=
{})
AuditEventService
.
new
(
user
,
user
,
options
).
for_authentication
.
security_event
end
end
end
app/controllers/profiles_controller.rb
View file @
411829fd
...
@@ -37,8 +37,11 @@ class ProfilesController < Profiles::ApplicationController
...
@@ -37,8 +37,11 @@ class ProfilesController < Profiles::ApplicationController
redirect_to
profile_account_path
redirect_to
profile_account_path
end
end
def
history
def
audit_log
@events
=
current_user
.
recent_events
.
page
(
params
[
:page
]).
per
(
PER_PAGE
)
@events
=
AuditEvent
.
where
(
entity_type:
"User"
,
entity_id:
current_user
.
id
).
order
(
"created_at DESC"
).
page
(
params
[
:page
]).
per
(
PER_PAGE
)
end
end
def
update_username
def
update_username
...
...
app/controllers/sessions_controller.rb
View file @
411829fd
...
@@ -37,6 +37,8 @@ class SessionsController < Devise::SessionsController
...
@@ -37,6 +37,8 @@ class SessionsController < Devise::SessionsController
resource
.
update_attributes
(
reset_password_token:
nil
,
resource
.
update_attributes
(
reset_password_token:
nil
,
reset_password_sent_at:
nil
)
reset_password_sent_at:
nil
)
end
end
authenticated_with
=
user_params
[
:otp_attempt
]
?
"two-factor"
:
"standard"
log_audit_event
(
current_user
,
with:
authenticated_with
)
end
end
end
end
...
@@ -95,4 +97,9 @@ class SessionsController < Devise::SessionsController
...
@@ -95,4 +97,9 @@ class SessionsController < Devise::SessionsController
user
.
valid_otp?
(
user_params
[
:otp_attempt
])
||
user
.
valid_otp?
(
user_params
[
:otp_attempt
])
||
user
.
invalidate_otp_backup_code!
(
user_params
[
:otp_attempt
])
user
.
invalidate_otp_backup_code!
(
user_params
[
:otp_attempt
])
end
end
def
log_audit_event
(
user
,
options
=
{})
AuditEventService
.
new
(
user
,
user
,
options
).
for_authentication
.
security_event
end
end
end
app/models/audit_event.rb
0 → 100644
View file @
411829fd
class
AuditEvent
<
ActiveRecord
::
Base
serialize
:details
,
Hash
belongs_to
:user
,
foreign_key: :author_id
validates
:author_id
,
presence:
true
validates
:entity_id
,
presence:
true
validates
:entity_type
,
presence:
true
after_initialize
:initialize_details
def
initialize_details
self
.
details
=
{}
if
details
.
nil?
end
def
author_name
self
.
user
.
name
end
end
app/models/security_event.rb
0 → 100644
View file @
411829fd
class
SecurityEvent
<
AuditEvent
end
app/services/audit_event_service.rb
0 → 100644
View file @
411829fd
class
AuditEventService
def
initialize
(
author
,
entity
,
details
=
{})
@author
,
@entity
,
@details
=
author
,
entity
,
details
end
def
for_authentication
@details
=
{
with:
@details
[
:with
],
target_id:
@author
.
id
,
target_type:
"User"
,
target_details:
@author
.
name
,
}
self
end
def
security_event
SecurityEvent
.
create
(
author_id:
@author
.
id
,
entity_id:
@entity
.
id
,
entity_type:
@entity
.
class
.
name
,
details:
@details
)
end
end
app/views/layouts/nav/_profile.html.haml
View file @
411829fd
...
@@ -44,8 +44,8 @@
...
@@ -44,8 +44,8 @@
=
icon
(
'image fw'
)
=
icon
(
'image fw'
)
%span
%span
Preferences
Preferences
=
nav_link
(
path:
'profiles#
history
'
)
do
=
nav_link
(
path:
'profiles#
audit_log
'
)
do
=
link_to
history_profile_path
,
title:
'History
'
,
data:
{
placement:
'right'
}
do
=
link_to
audit_log_profile_path
,
title:
'Audit Log
'
,
data:
{
placement:
'right'
}
do
=
icon
(
'history fw'
)
=
icon
(
'history fw'
)
%span
%span
History
Audit Log
app/views/profiles/_event_table.html.haml
0 → 100644
View file @
411829fd
%table
.table
#audits
%thead
%tr
%th
Action
%th
When
%tbody
-
events
.
each
do
|
event
|
%tr
%td
%span
Signed in with
%b
=
event
.
details
[
:with
]
authentication
%td
#{
time_ago_in_words
event
.
created_at
}
ago
=
paginate
events
,
theme:
"gitlab"
app/views/profiles/audit_log.html.haml
0 → 100644
View file @
411829fd
-
page_title
"Audit Log"
%h3
.page-title
Audit Log
%p
.light
History of authentications
=
render
'event_table'
,
events:
@events
\ No newline at end of file
app/views/profiles/history.html.haml
deleted
100644 → 0
View file @
8ba83cba
-
page_title
"History"
%h3
.page-title
Your Account History
%p
.light
All events created by your account are listed below.
%hr
.profile_history
=
render
@events
%hr
=
paginate
@events
,
theme:
"gitlab"
config/routes.rb
View file @
411829fd
...
@@ -207,7 +207,7 @@ Gitlab::Application.routes.draw do
...
@@ -207,7 +207,7 @@ Gitlab::Application.routes.draw do
#
#
resource
:profile
,
only:
[
:show
,
:update
]
do
resource
:profile
,
only:
[
:show
,
:update
]
do
member
do
member
do
get
:
history
get
:
audit_log
get
:applications
get
:applications
put
:reset_private_token
put
:reset_private_token
...
...
db/migrate/20141118150935_add_audit_event.rb
0 → 100644
View file @
411829fd
class
AddAuditEvent
<
ActiveRecord
::
Migration
def
change
create_table
:audit_events
do
|
t
|
t
.
integer
:author_id
,
null:
false
t
.
string
:type
,
null:
false
# "Namespace" where the change occurs
# eg. On a project, group or user
t
.
integer
:entity_id
,
null:
false
t
.
string
:entity_type
,
null:
false
# Details for the event
t
.
text
:details
t
.
timestamps
end
add_index
:audit_events
,
:author_id
add_index
:audit_events
,
:type
add_index
:audit_events
,
[
:entity_id
,
:entity_type
]
end
end
db/schema.rb
View file @
411829fd
...
@@ -28,16 +28,30 @@ ActiveRecord::Schema.define(version: 20150620233230) do
...
@@ -28,16 +28,30 @@ ActiveRecord::Schema.define(version: 20150620233230) do
t
.
integer
"default_branch_protection"
,
default:
2
t
.
integer
"default_branch_protection"
,
default:
2
t
.
boolean
"twitter_sharing_enabled"
,
default:
true
t
.
boolean
"twitter_sharing_enabled"
,
default:
true
t
.
text
"restricted_visibility_levels"
t
.
text
"restricted_visibility_levels"
t
.
boolean
"version_check_enabled"
,
default:
true
t
.
integer
"max_attachment_size"
,
default:
10
,
null:
false
t
.
integer
"max_attachment_size"
,
default:
10
,
null:
false
t
.
integer
"default_project_visibility"
t
.
integer
"default_project_visibility"
t
.
integer
"default_snippet_visibility"
t
.
integer
"default_snippet_visibility"
t
.
text
"restricted_signup_domains"
t
.
text
"restricted_signup_domains"
t
.
boolean
"version_check_enabled"
,
default:
true
t
.
boolean
"user_oauth_applications"
,
default:
true
t
.
boolean
"user_oauth_applications"
,
default:
true
t
.
string
"after_sign_out_path"
t
.
string
"after_sign_out_path"
t
.
integer
"session_expire_delay"
,
default:
10080
,
null:
false
t
.
integer
"session_expire_delay"
,
default:
10080
,
null:
false
end
end
create_table
"audit_events"
,
force:
true
do
|
t
|
t
.
integer
"author_id"
,
null:
false
t
.
string
"type"
,
null:
false
t
.
integer
"entity_id"
,
null:
false
t
.
string
"entity_type"
,
null:
false
t
.
text
"details"
t
.
datetime
"created_at"
t
.
datetime
"updated_at"
end
add_index
"audit_events"
,
[
"author_id"
],
name:
"index_audit_events_on_author_id"
,
using: :btree
add_index
"audit_events"
,
[
"entity_id"
,
"entity_type"
],
name:
"index_audit_events_on_entity_id_and_entity_type"
,
using: :btree
add_index
"audit_events"
,
[
"type"
],
name:
"index_audit_events_on_type"
,
using: :btree
create_table
"broadcast_messages"
,
force:
true
do
|
t
|
create_table
"broadcast_messages"
,
force:
true
do
|
t
|
t
.
text
"message"
,
null:
false
t
.
text
"message"
,
null:
false
t
.
datetime
"starts_at"
t
.
datetime
"starts_at"
...
@@ -496,12 +510,12 @@ ActiveRecord::Schema.define(version: 20150620233230) do
...
@@ -496,12 +510,12 @@ ActiveRecord::Schema.define(version: 20150620233230) do
t
.
string
"bitbucket_access_token"
t
.
string
"bitbucket_access_token"
t
.
string
"bitbucket_access_token_secret"
t
.
string
"bitbucket_access_token_secret"
t
.
string
"location"
t
.
string
"location"
t
.
string
"public_email"
,
default:
""
,
null:
false
t
.
string
"encrypted_otp_secret"
t
.
string
"encrypted_otp_secret"
t
.
string
"encrypted_otp_secret_iv"
t
.
string
"encrypted_otp_secret_iv"
t
.
string
"encrypted_otp_secret_salt"
t
.
string
"encrypted_otp_secret_salt"
t
.
boolean
"otp_required_for_login"
,
default:
false
,
null:
false
t
.
boolean
"otp_required_for_login"
,
default:
false
,
null:
false
t
.
text
"otp_backup_codes"
t
.
text
"otp_backup_codes"
t
.
string
"public_email"
,
default:
""
,
null:
false
t
.
integer
"dashboard"
,
default:
0
t
.
integer
"dashboard"
,
default:
0
end
end
...
...
features/profile/active_tab.feature
View file @
411829fd
...
@@ -23,7 +23,7 @@ Feature: Profile Active Tab
...
@@ -23,7 +23,7 @@ Feature: Profile Active Tab
Then
the active main tab should be Preferences
Then
the active main tab should be Preferences
And
no other main tabs should be active
And
no other main tabs should be active
Scenario
:
On Profile
History
Scenario
:
On Profile
Audit Log
Given
I visit
profile history
page
Given
I visit
Audit Log
page
Then
the active main tab should be
History
Then
the active main tab should be
Audit Log
And
no other main tabs should be active
And
no other main tabs should be active
features/profile/profile.feature
View file @
411829fd
...
@@ -63,7 +63,7 @@ Feature: Profile
...
@@ -63,7 +63,7 @@ Feature: Profile
Scenario
:
I
visit history tab
Scenario
:
I
visit history tab
Given
I have activity
Given
I have activity
When
I visit
profile history
page
When
I visit
Audit Log
page
Then
I should see my activity
Then
I should see my activity
Scenario
:
I
visit my user page
Scenario
:
I
visit my user page
...
...
features/steps/profile/active_tab.rb
View file @
411829fd
...
@@ -19,7 +19,7 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
...
@@ -19,7 +19,7 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
ensure_active_main_tab
(
'Preferences'
)
ensure_active_main_tab
(
'Preferences'
)
end
end
step
'the active main tab should be
History
'
do
step
'the active main tab should be
Audit Log
'
do
ensure_active_main_tab
(
'
History
'
)
ensure_active_main_tab
(
'
Audit Log
'
)
end
end
end
end
features/steps/profile/profile.rb
View file @
411829fd
...
@@ -115,7 +115,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
...
@@ -115,7 +115,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
end
step
'I should see my activity'
do
step
'I should see my activity'
do
expect
(
page
).
to
have_content
"
#{
current_user
.
name
}
closed issue
"
expect
(
page
).
to
have_content
"
Signed in with standard authentication
"
end
end
step
'my password is expired'
do
step
'my password is expired'
do
...
...
features/steps/shared/paths.rb
View file @
411829fd
...
@@ -127,8 +127,8 @@ module SharedPaths
...
@@ -127,8 +127,8 @@ module SharedPaths
visit
profile_preferences_path
visit
profile_preferences_path
end
end
step
'I visit
profile history
page'
do
step
'I visit
Audit Log
page'
do
visit
history
_profile_path
visit
audit_log
_profile_path
end
end
# ----------------------------------------
# ----------------------------------------
...
...
spec/features/security/profile_access_spec.rb
View file @
411829fd
...
@@ -45,8 +45,8 @@ describe "Profile access", feature: true do
...
@@ -45,8 +45,8 @@ describe "Profile access", feature: true do
it
{
is_expected
.
to
be_denied_for
:visitor
}
it
{
is_expected
.
to
be_denied_for
:visitor
}
end
end
describe
"GET /profile/
history
"
do
describe
"GET /profile/
audit_log
"
do
subject
{
history
_profile_path
}
subject
{
audit_log
_profile_path
}
it
{
is_expected
.
to
be_allowed_for
@u1
}
it
{
is_expected
.
to
be_allowed_for
@u1
}
it
{
is_expected
.
to
be_allowed_for
:admin
}
it
{
is_expected
.
to
be_allowed_for
:admin
}
...
...
spec/routing/routing_spec.rb
View file @
411829fd
...
@@ -108,8 +108,8 @@ describe ProfilesController, "routing" do
...
@@ -108,8 +108,8 @@ describe ProfilesController, "routing" do
expect
(
get
(
"/profile/account"
)).
to
route_to
(
'profiles/accounts#show'
)
expect
(
get
(
"/profile/account"
)).
to
route_to
(
'profiles/accounts#show'
)
end
end
it
"to #
history
"
do
it
"to #
audit_log
"
do
expect
(
get
(
"/profile/
history"
)).
to
route_to
(
'profiles#history
'
)
expect
(
get
(
"/profile/
audit_log"
)).
to
route_to
(
'profiles#audit_log
'
)
end
end
it
"to #reset_private_token"
do
it
"to #reset_private_token"
do
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment