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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
df01bc07
Commit
df01bc07
authored
Jun 06, 2017
by
James Lopez
Committed by
Sean McGivern
Jun 06, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement server-wide Audit Logging admin screen
parent
0bd4449d
Changes
24
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
628 additions
and
27 deletions
+628
-27
app/assets/javascripts/audit_logs.js
app/assets/javascripts/audit_logs.js
+50
-0
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+4
-0
app/assets/javascripts/project_select.js
app/assets/javascripts/project_select.js
+29
-20
app/controllers/admin/audit_logs_controller.rb
app/controllers/admin/audit_logs_controller.rb
+19
-0
app/finders/log_finder.rb
app/finders/log_finder.rb
+34
-0
app/helpers/audit_logs_helper.rb
app/helpers/audit_logs_helper.rb
+34
-0
app/models/audit_event.rb
app/models/audit_event.rb
+6
-2
app/presenters/audit_event_presenter.rb
app/presenters/audit_event_presenter.rb
+28
-0
app/services/audit_event_service.rb
app/services/audit_event_service.rb
+3
-2
app/views/admin/audit_logs/index.html.haml
app/views/admin/audit_logs/index.html.haml
+49
-0
app/views/admin/background_jobs/_head.html.haml
app/views/admin/background_jobs/_head.html.haml
+4
-0
app/views/layouts/nav/_admin.html.haml
app/views/layouts/nav/_admin.html.haml
+2
-2
changelogs/unreleased-ee/feature-server-wide-audit-logging.yml
...elogs/unreleased-ee/feature-server-wide-audit-logging.yml
+4
-0
config/routes/admin.rb
config/routes/admin.rb
+1
-0
doc/administration/audit_events.md
doc/administration/audit_events.md
+18
-0
doc/administration/audit_log.png
doc/administration/audit_log.png
+0
-0
lib/audit/details.rb
lib/audit/details.rb
+37
-0
spec/factories/audit_events.rb
spec/factories/audit_events.rb
+22
-0
spec/features/admin/admin_audit_logs_spec.rb
spec/features/admin/admin_audit_logs_spec.rb
+89
-0
spec/finders/log_finder_spec.rb
spec/finders/log_finder_spec.rb
+55
-0
spec/lib/audit/details_spec.rb
spec/lib/audit/details_spec.rb
+79
-0
spec/models/audit_event_spec.rb
spec/models/audit_event_spec.rb
+6
-0
spec/presenters/audit_event_presenter_spec.rb
spec/presenters/audit_event_presenter_spec.rb
+44
-0
spec/services/audit_event_service_spec.rb
spec/services/audit_event_service_spec.rb
+11
-1
No files found.
app/assets/javascripts/audit_logs.js
0 → 100644
View file @
df01bc07
/* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props, no-new */
/* global GroupsSelect */
/* global ProjectSelect */
import
UsersSelect
from
'
./users_select
'
;
import
'
./groups_select
'
;
import
'
./project_select
'
;
class
AuditLogs
{
constructor
()
{
this
.
initFilters
();
}
initFilters
()
{
new
ProjectSelect
();
new
GroupsSelect
();
new
UsersSelect
();
this
.
initFilterDropdown
(
$
(
'
.js-type-filter
'
),
'
event_type
'
,
null
,
()
=>
{
$
(
'
.hidden-filter-value
'
).
val
(
''
);
$
(
'
form.filter-form
'
).
submit
();
});
$
(
'
.project-item-select
'
).
on
(
'
click
'
,
()
=>
{
$
(
'
form.filter-form
'
).
submit
();
});
$
(
'
form.filter-form
'
).
on
(
'
submit
'
,
function
applyFilters
(
event
)
{
event
.
preventDefault
();
gl
.
utils
.
visitUrl
(
`
${
this
.
action
}
?
${
$
(
this
).
serialize
()}
`
);
});
}
initFilterDropdown
(
$dropdown
,
fieldName
,
searchFields
,
cb
)
{
const
dropdownOptions
=
{
fieldName
,
selectable
:
true
,
filterable
:
searchFields
?
true
:
false
,
search
:
{
fields
:
searchFields
},
data
:
$dropdown
.
data
(
'
data
'
),
clicked
:
()
=>
$dropdown
.
closest
(
'
form.filter-form
'
).
submit
(),
};
if
(
cb
)
{
dropdownOptions
.
clicked
=
cb
;
}
$dropdown
.
glDropdown
(
dropdownOptions
);
}
}
export
default
AuditLogs
;
app/assets/javascripts/dispatcher.js
View file @
df01bc07
...
...
@@ -61,6 +61,7 @@ import ShortcutsBlob from './shortcuts_blob';
// EE-only
import
ApproversSelect
from
'
./approvers_select
'
;
import
AuditLogs
from
'
./audit_logs
'
;
(
function
()
{
var
Dispatcher
;
...
...
@@ -394,6 +395,9 @@ import ApproversSelect from './approvers_select';
case
'
admin:emails:show
'
:
new
AdminEmailSelect
();
break
;
case
'
admin:audit_logs:index
'
:
new
AuditLogs
();
break
;
case
'
projects:repository:show
'
:
// Initialize Protected Branch Settings
new
gl
.
ProtectedBranchCreate
();
...
...
app/assets/javascripts/project_select.js
View file @
df01bc07
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-else-return, quotes, max-len */
import
Api
from
'
./api
'
;
(
function
()
{
this
.
ProjectSelect
=
(
function
()
{
(
function
()
{
this
.
ProjectSelect
=
(
function
()
{
function
ProjectSelect
()
{
$
(
'
.js-projects-dropdown-toggle
'
).
each
(
function
(
i
,
dropdown
)
{
$
(
'
.js-projects-dropdown-toggle
'
).
each
(
function
(
i
,
dropdown
)
{
var
$dropdown
;
$dropdown
=
$
(
dropdown
);
return
$dropdown
.
glDropdown
({
...
...
@@ -13,16 +13,16 @@ import Api from './api';
search
:
{
fields
:
[
'
name_with_namespace
'
]
},
data
:
function
(
term
,
callback
)
{
data
:
function
(
term
,
callback
)
{
var
finalCallback
,
projectsCallback
;
var
orderBy
=
$dropdown
.
data
(
'
order-by
'
);
finalCallback
=
function
(
projects
)
{
finalCallback
=
function
(
projects
)
{
return
callback
(
projects
);
};
if
(
this
.
includeGroups
)
{
projectsCallback
=
function
(
projects
)
{
projectsCallback
=
function
(
projects
)
{
var
groupsCallback
;
groupsCallback
=
function
(
groups
)
{
groupsCallback
=
function
(
groups
)
{
var
data
;
data
=
groups
.
concat
(
projects
);
return
finalCallback
(
data
);
...
...
@@ -35,22 +35,28 @@ import Api from './api';
if
(
this
.
groupId
)
{
return
Api
.
groupProjects
(
this
.
groupId
,
term
,
projectsCallback
);
}
else
{
return
Api
.
projects
(
term
,
{
order_by
:
orderBy
},
projectsCallback
);
return
Api
.
projects
(
term
,
{
order_by
:
orderBy
},
projectsCallback
);
}
},
url
:
function
(
project
)
{
url
:
function
(
project
)
{
return
project
.
web_url
;
},
text
:
function
(
project
)
{
text
:
function
(
project
)
{
return
project
.
name_with_namespace
;
}
});
});
$
(
'
.ajax-project-select
'
).
each
(
function
(
i
,
select
)
{
$
(
'
.ajax-project-select
'
).
each
(
function
(
i
,
select
)
{
var
placeholder
;
var
idAttribute
;
this
.
groupId
=
$
(
select
).
data
(
'
group-id
'
);
this
.
includeGroups
=
$
(
select
).
data
(
'
include-groups
'
);
this
.
allProjects
=
$
(
select
).
data
(
'
allprojects
'
)
||
false
;
this
.
orderBy
=
$
(
select
).
data
(
'
order-by
'
)
||
'
id
'
;
idAttribute
=
$
(
select
).
data
(
'
idattribute
'
)
||
'
web_url
'
;
placeholder
=
"
Search for project
"
;
if
(
this
.
includeGroups
)
{
placeholder
+=
"
or group
"
;
...
...
@@ -58,10 +64,10 @@ import Api from './api';
return
$
(
select
).
select2
({
placeholder
:
placeholder
,
minimumInputLength
:
0
,
query
:
(
function
(
_this
)
{
return
function
(
query
)
{
query
:
(
function
(
_this
)
{
return
function
(
query
)
{
var
finalCallback
,
projectsCallback
;
finalCallback
=
function
(
projects
)
{
finalCallback
=
function
(
projects
)
{
var
data
;
data
=
{
results
:
projects
...
...
@@ -69,9 +75,9 @@ import Api from './api';
return
query
.
callback
(
data
);
};
if
(
_this
.
includeGroups
)
{
projectsCallback
=
function
(
projects
)
{
projectsCallback
=
function
(
projects
)
{
var
groupsCallback
;
groupsCallback
=
function
(
groups
)
{
groupsCallback
=
function
(
groups
)
{
var
data
;
data
=
groups
.
concat
(
projects
);
return
finalCallback
(
data
);
...
...
@@ -84,14 +90,17 @@ import Api from './api';
if
(
_this
.
groupId
)
{
return
Api
.
groupProjects
(
_this
.
groupId
,
query
.
term
,
projectsCallback
);
}
else
{
return
Api
.
projects
(
query
.
term
,
{
order_by
:
_this
.
orderBy
},
projectsCallback
);
return
Api
.
projects
(
query
.
term
,
{
order_by
:
_this
.
orderBy
,
membership
:
!
_this
.
allProjects
},
projectsCallback
);
}
};
})(
this
),
id
:
function
(
project
)
{
return
project
.
web_url
;
id
:
function
(
project
)
{
return
project
[
idAttribute
]
;
},
text
:
function
(
project
)
{
text
:
function
(
project
)
{
return
project
.
name_with_namespace
||
project
.
name
;
},
dropdownCssClass
:
"
ajax-project-dropdown
"
...
...
app/controllers/admin/audit_logs_controller.rb
0 → 100644
View file @
df01bc07
class
Admin::AuditLogsController
<
Admin
::
ApplicationController
def
index
@events
=
LogFinder
.
new
(
audit_logs_params
).
execute
@entity
=
case
audit_logs_params
[
:event_type
]
when
'User'
User
.
find_by_id
(
audit_logs_params
[
:user_id
])
when
'Project'
Project
.
find_by_id
(
audit_logs_params
[
:project_id
])
when
'Group'
Namespace
.
find_by_id
(
audit_logs_params
[
:group_id
])
else
nil
end
end
def
audit_logs_params
params
.
permit
(
:page
,
:event_type
,
:user_id
,
:project_id
,
:group_id
)
end
end
app/finders/log_finder.rb
0 → 100644
View file @
df01bc07
class
LogFinder
PER_PAGE
=
25
ENTITY_COLUMN_TYPES
=
{
'User'
=>
:user_id
,
'Group'
=>
:group_id
,
'Project'
=>
:project_id
}.
freeze
def
initialize
(
params
)
@params
=
params
end
def
execute
AuditEvent
.
order
(
id: :desc
).
where
(
conditions
).
page
(
@params
[
:page
]).
per
(
PER_PAGE
)
end
private
def
conditions
return
nil
unless
entity_column
{
entity_type:
@params
[
:event_type
]
}.
tap
do
|
hash
|
hash
[
:entity_id
]
=
@params
[
entity_column
]
if
entity_present?
end
end
def
entity_column
@entity_column
||=
ENTITY_COLUMN_TYPES
[
@params
[
:event_type
]]
end
def
entity_present?
@params
[
entity_column
]
&&
@params
[
entity_column
]
!=
'0'
end
end
app/helpers/audit_logs_helper.rb
0 → 100644
View file @
df01bc07
module
AuditLogsHelper
def
event_type_options
[
{
id:
''
,
text:
'All Events'
},
{
id:
'Group'
,
text:
'Group Events'
},
{
id:
'Project'
,
text:
'Project Events'
},
{
id:
'User'
,
text:
'User Events'
}
]
end
def
admin_user_dropdown_label
(
default_label
)
if
@entity
@entity
.
name
else
default_label
end
end
def
admin_project_dropdown_label
(
default_label
)
if
@entity
@entity
.
name_with_namespace
else
default_label
end
end
def
admin_namespace_dropdown_label
(
default_label
)
if
@entity
@entity
.
full_path
else
default_label
end
end
end
app/models/audit_event.rb
View file @
df01bc07
...
...
@@ -9,11 +9,15 @@ class AuditEvent < ActiveRecord::Base
after_initialize
:initialize_details
def
author_name
details
[
:author_name
].
blank?
?
user
&
.
name
:
details
[
:author_name
]
end
def
initialize_details
self
.
details
=
{}
if
details
.
nil?
end
def
author_name
self
.
user
.
try
(
:name
)
||
details
[
:author_name
]
def
present
AuditEventPresenter
.
new
(
self
)
end
end
app/presenters/audit_event_presenter.rb
0 → 100644
View file @
df01bc07
class
AuditEventPresenter
<
Gitlab
::
View
::
Presenter
::
Simple
presents
:audit_event
def
author_name
audit_event
.
author_name
||
'(removed)'
end
def
target
audit_event
.
details
[
:target_details
]
end
def
ip_address
audit_event
.
details
[
:ip_address
]
end
def
object
audit_event
.
details
[
:entity_path
]
end
def
date
audit_event
.
created_at
.
to_s
(
:db
)
end
def
action
Audit
::
Details
.
humanize
(
audit_event
.
details
)
end
end
app/services/audit_event_service.rb
View file @
df01bc07
...
...
@@ -29,7 +29,7 @@ class AuditEventService
target_type:
"User"
,
target_details:
user_name
}
when
:update
when
:update
,
:override
{
change:
"access_level"
,
from:
old_access_level
,
...
...
@@ -87,7 +87,8 @@ class AuditEventService
author_id:
@author
.
id
,
entity_id:
@entity
.
id
,
entity_type:
@entity
.
class
.
name
,
details:
@details
details:
@details
.
merge
(
ip_address:
@author
.
current_sign_in_ip
,
entity_path:
@entity
.
full_path
)
)
end
end
app/views/admin/audit_logs/index.html.haml
0 → 100644
View file @
df01bc07
-
@no_container
=
true
-
page_title
'Audit Log'
=
render
'admin/background_jobs/head'
%div
{
class:
container_class
}
.todos-filters
.row-content-block.second-block
=
form_tag
admin_audit_logs_path
,
method: :get
,
class:
'filter-form'
do
.filter-item.inline
-
if
params
[
:event_type
].
present?
=
hidden_field_tag
(
:event_type
,
params
[
:event_type
])
-
event_type
=
params
[
:event_type
].
presence
||
'All'
=
dropdown_tag
(
"
#{
event_type
}
Events"
,
options:
{
toggle_class:
'js-type-search js-filter-submit js-type-filter'
,
dropdown_class:
'dropdown-menu-type dropdown-menu-selectable dropdown-menu-action js-filter-submit'
,
placeholder:
'Search types'
,
data:
{
field_name:
'event_type'
,
data:
event_type_options
,
default_label:
'All Events'
}
})
-
if
params
[
:event_type
]
==
'User'
.filter-item.inline
-
if
params
[
:user_id
].
present?
=
hidden_field_tag
(
:user_id
,
params
[
:user_id
],
class
:'hidden-filter-value'
)
=
dropdown_tag
(
admin_user_dropdown_label
(
'User'
),
options:
{
toggle_class:
'js-user-search js-filter-submit'
,
filter:
true
,
dropdown_class:
'dropdown-menu-user dropdown-menu-selectable'
,
placeholder:
'Search users'
,
data:
{
first_user:
(
current_user
.
username
if
current_user
),
null_user:
true
,
current_user:
true
,
field_name:
'user_id'
}
})
-
elsif
params
[
:event_type
]
==
'Project'
.filter-item.inline
=
project_select_tag
(
:project_id
,
{
class:
'project-item-select hidden-filter-value'
,
toggle_class:
'js-project-search js-project-filter js-filter-submit'
,
dropdown_class:
'dropdown-menu-selectable dropdown-menu-project js-filter-submit'
,
placeholder:
admin_project_dropdown_label
(
'Search projects'
),
idAttribute:
'id'
,
data:
{
order_by:
'last_activity_at'
,
idattribute:
'id'
,
allprojects:
'true'
}
})
-
elsif
params
[
:event_type
]
==
'Group'
.filter-item.inline
=
groups_select_tag
(
:group_id
,
{
required:
true
,
class:
'group-item-select project-item-select hidden-filter-value'
,
toggle_class:
'js-group-search js-group-filter js-filter-submit'
,
dropdown_class:
'dropdown-menu-selectable dropdown-menu-group js-filter-submit'
,
placeholder:
admin_namespace_dropdown_label
(
'Search groups'
),
idAttribute:
'id'
,
data:
{
order_by:
'last_activity_at'
,
idattribute:
'id'
,
all_available:
true
}
})
-
if
@events
.
present?
%table
.table
%thead
%tr
%th
Author
%th
Object
%th
Action
%th
Target
%th
IP Address
%th
Date
%tbody
-
@events
.
map
(
&
:present
).
each
do
|
event
|
%tr
%td
=
event
.
author_name
%td
=
event
.
object
%td
=
event
.
action
%td
=
event
.
target
%td
=
event
.
ip_address
%td
=
event
.
date
=
paginate
@events
,
theme:
'gitlab'
app/views/admin/background_jobs/_head.html.haml
View file @
df01bc07
...
...
@@ -23,3 +23,7 @@
=
link_to
admin_requests_profiles_path
,
title:
'Requests Profiles'
do
%span
Requests Profiles
=
nav_link
path:
'audit_logs#index'
do
=
link_to
admin_audit_logs_path
,
title:
'Audit Log'
do
%span
Audit Log
app/views/layouts/nav/_admin.html.haml
View file @
df01bc07
...
...
@@ -5,11 +5,11 @@
.fade-right
=
icon
(
'angle-right'
)
%ul
.nav-links.scrolling-tabs
=
nav_link
(
controller:
%w(dashboard admin projects users groups builds runners)
,
html_options:
{
class:
'home'
})
do
=
nav_link
(
controller:
%w(dashboard admin projects users groups builds runners
cohorts
)
,
html_options:
{
class:
'home'
})
do
=
link_to
admin_root_path
,
title:
'Overview'
,
class:
'shortcuts-tree'
do
%span
Overview
=
nav_link
(
controller:
%w(system_info background_jobs logs health_check requests_profiles)
)
do
=
nav_link
(
controller:
%w(system_info background_jobs logs health_check requests_profiles
audit_logs
)
)
do
=
link_to
admin_system_info_path
,
title:
'Monitoring'
do
%span
Monitoring
...
...
changelogs/unreleased-ee/feature-server-wide-audit-logging.yml
0 → 100644
View file @
df01bc07
---
title
:
Add server-wide Audit Log admin screen
merge_request
:
1852
author
:
config/routes/admin.rb
View file @
df01bc07
...
...
@@ -76,6 +76,7 @@ namespace :admin do
## EE-specific
resource
:email
,
only:
[
:show
,
:create
]
resources
:audit_logs
,
controller:
'audit_logs'
,
only:
[
:index
]
## EE-specific
resource
:system_info
,
controller:
'system_info'
,
only:
[
:show
]
...
...
doc/administration/audit_events.md
View file @
df01bc07
...
...
@@ -28,3 +28,21 @@ To view the Audit Events user needs to have enough permissions to view the group
Navigate to Group->Settings->Audit Events to view the Audit Events:
![
audit events group
](
audit_events_group.png
)
# Audit Log (Admin only)
> **Notes:**
> [Introduced][ee-2336] in GitLab 9.3.
Server-wide audit logging, available in GitLab Enterprise Edition Premium since 9.3, introduces
the ability to observe user actions across the entire instance of your GitLab Server, making it
easy to understand who changed what and when for audit purposes.
To view the server-wide admin log, visit the Admin Area, select Monitoring and choose Audit Log.
It is possible to filter particular actions by choosing an audit data type from the filter drop-down.
You can further filter by specific group, project or user (for authentication events).
![
audit log
](
audit_log.png
)
[
ce-23361
]:
https://gitlab.com/gitlab-org/gitlab-ee/issues/2336
doc/administration/audit_log.png
0 → 100644
View file @
df01bc07
120 KB
lib/audit/details.rb
0 → 100644
View file @
df01bc07
module
Audit
class
Details
CRUD_ACTIONS
=
%i[add remove change]
.
freeze
def
self
.
humanize
(
*
args
)
new
(
*
args
).
humanize
end
def
initialize
(
details
)
@details
=
details
end
def
humanize
if
@details
[
:with
]
"Signed in with
#{
@details
[
:with
].
upcase
}
authentication"
else
crud_action_text
end
end
private
def
crud_action_text
action
=
@details
.
slice
(
*
CRUD_ACTIONS
)
value
=
@details
.
values
.
first
.
tr
(
'_'
,
' '
)
case
action
.
keys
.
first
when
:add
"Added
#{
value
}#{
@details
[
:as
]
?
" as
#{
@details
[
:as
]
}
"
:
""
}
"
when
:remove
"Removed
#{
value
}
"
else
"Changed
#{
value
}
from
#{
@details
[
:from
]
}
to
#{
@details
[
:to
]
}
"
end
end
end
end
spec/factories/audit_events.rb
0 → 100644
View file @
df01bc07
FactoryGirl
.
define
do
factory
:audit_event
,
aliases:
[
:user_audit_event
]
do
user
type
'SecurityEvent'
entity_type
'User'
entity_id
{
user
.
id
}
trait
:project_event
do
entity_type
'Project'
entity_id
{
create
(
:empty_project
).
id
}
end
trait
:group_event
do
entity_type
'Group'
entity_id
{
create
(
:group
).
id
}
end
factory
:project_audit_event
,
traits:
[
:project_event
]
factory
:group_audit_event
,
traits:
[
:group_event
]
end
end
spec/features/admin/admin_audit_logs_spec.rb
0 → 100644
View file @
df01bc07
require
'spec_helper'
describe
'Admin::AuditLogs'
,
feature:
true
,
js:
true
do
include
Select2Helper
let
(
:user
)
{
create
(
:user
)
}
before
do
login_as
:admin
end
describe
'user events'
do
before
do
AuditEventService
.
new
(
user
,
user
,
with: :ldap
).
for_authentication
.
security_event
visit
admin_audit_logs_path
end
it
'filters by user'
do
filter_by_type
(
'User Events'
)
click_button
'User'
wait_for_requests
within
'.dropdown-menu-user'
do
click_link
user
.
name
end
wait_for_requests
expect
(
page
).
to
have_content
(
'Signed in with LDAP authentication'
)
end
end
describe
'group events'
do
let
(
:group_member
)
{
create
(
:group_member
,
user:
user
)
}
before
do
AuditEventService
.
new
(
user
,
group_member
.
group
,
{
action: :create
}).
for_member
(
group_member
).
security_event
visit
admin_audit_logs_path
end
it
'filters by group'
do
filter_by_type
(
'Group Events'
)
click_button
'Group'
find
(
'.group-item-select'
).
click
wait_for_requests
find
(
'.select2-results'
).
click
expect
(
page
).
to
have_content
(
'Added user access as Owner'
)
end
end
describe
'project events'
do
let
(
:project
)
{
create
(
:empty_project
)
}
let
(
:project_member
)
{
create
(
:project_member
,
user:
user
)
}
before
do
AuditEventService
.
new
(
user
,
project
,
{
action: :destroy
}).
for_member
(
project_member
).
security_event
visit
admin_audit_logs_path
end
it
'filters by project'
do
filter_by_type
(
'Project Events'
)
click_button
'Project'
find
(
'.project-item-select'
).
click
wait_for_requests
find
(
'.select2-results'
).
click
expect
(
page
).
to
have_content
(
'Removed user access'
)
end
end
def
filter_by_type
(
type
)
click_button
'Events'
within
'.dropdown-menu-type'
do
click_link
type
end
wait_for_requests
end
end
spec/finders/log_finder_spec.rb
0 → 100644
View file @
df01bc07
require
'spec_helper'
describe
LogFinder
do
let
(
:user
)
{
create
(
:user
)
}
describe
'#execute'
do
before
do
create
(
:user_audit_event
)
create
(
:project_audit_event
)
create
(
:group_audit_event
)
end
it
'finds all the events'
do
expect
(
described_class
.
new
({}).
execute
.
count
).
to
eq
(
3
)
end
context
'filtering by ID'
do
it
'finds the right user event'
do
expect
(
described_class
.
new
(
event_type:
'User'
,
user_id:
1
).
execute
.
map
(
&
:entity_type
)).
to
all
(
eq
'User'
)
end
it
'finds the right project event'
do
expect
(
described_class
.
new
(
event_type:
'Project'
,
project_id:
1
).
execute
.
map
(
&
:entity_type
)).
to
all
(
eq
'Project'
)
end
it
'finds the right group event'
do
expect
(
described_class
.
new
(
event_type:
'Group'
,
group_id:
1
).
execute
.
map
(
&
:entity_type
)).
to
all
(
eq
'Group'
)
end
end
context
'filtering by type'
do
it
'finds the right user event'
do
expect
(
described_class
.
new
(
event_type:
'User'
).
execute
.
map
(
&
:entity_type
)).
to
all
(
eq
'User'
)
end
it
'finds the right project event'
do
expect
(
described_class
.
new
(
event_type:
'Project'
).
execute
.
map
(
&
:entity_type
)).
to
all
(
eq
'Project'
)
end
it
'finds the right group event'
do
expect
(
described_class
.
new
(
event_type:
'Group'
).
execute
.
map
(
&
:entity_type
)).
to
all
(
eq
'Group'
)
end
it
'finds all the events with no valid even type'
do
expect
(
described_class
.
new
(
event_type:
''
).
execute
.
count
).
to
eq
(
3
)
end
end
end
end
spec/lib/audit/details_spec.rb
0 → 100644
View file @
df01bc07
require
'spec_helper'
describe
Audit
::
Details
do
let
(
:user
)
{
create
(
:user
)
}
describe
'.humanize'
do
context
'user'
do
let
(
:login_action
)
do
{
with: :ldap
,
target_id:
user
.
id
,
target_type:
'User'
,
target_details:
user
.
name
}
end
it
'humanizes user login action'
do
expect
(
described_class
.
humanize
(
login_action
)).
to
eq
(
'Signed in with LDAP authentication'
)
end
end
context
'project'
do
let
(
:user_member
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:empty_project
)
}
let
(
:member
)
{
create
(
:project_member
,
:developer
,
user:
user_member
,
project:
project
)
}
let
(
:member_access_action
)
do
{
add:
'user_access'
,
as:
Gitlab
::
Access
.
options_with_owner
.
key
(
member
.
access_level
.
to_i
),
author_name:
user
.
name
,
target_id:
member
.
id
,
target_type:
'User'
,
target_details:
member
.
user
.
name
}
end
it
'humanizes add project member access action'
do
expect
(
described_class
.
humanize
(
member_access_action
)).
to
eq
(
'Added user access as Developer'
)
end
end
context
'group'
do
let
(
:user_member
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:member
)
{
create
(
:group_member
,
group:
group
,
user:
user_member
)
}
let
(
:member_access_action
)
do
{
change:
'access_level'
,
from:
'Guest'
,
to:
member
.
human_access
,
author_name:
user
.
name
,
target_id:
member
.
id
,
target_type:
'User'
,
target_details:
member
.
user
.
name
}
end
it
'humanizes add group member access action'
do
expect
(
described_class
.
humanize
(
member_access_action
)).
to
eq
(
'Changed access level from Guest to Owner'
)
end
end
context
'deploy key'
do
let
(
:removal_action
)
do
{
remove:
'deploy_key'
,
author_name:
user
.
name
,
target_id:
'key title'
,
target_type:
'DeployKey'
,
target_details:
'key title'
}
end
it
'humanizes the removal action'
do
expect
(
described_class
.
humanize
(
removal_action
)).
to
eq
(
'Removed deploy key'
)
end
end
end
end
spec/models/audit_event_spec.rb
View file @
df01bc07
...
...
@@ -42,4 +42,10 @@ RSpec.describe AuditEvent, type: :model do
end
end
end
describe
'#present'
do
it
'returns a presenter'
do
expect
(
subject
.
present
).
to
be_an_instance_of
(
AuditEventPresenter
)
end
end
end
spec/presenters/audit_event_presenter_spec.rb
0 → 100644
View file @
df01bc07
require
'spec_helper'
describe
AuditEventPresenter
do
let
(
:details
)
do
{
author_name:
'author'
,
ip_address:
'127.0.0.1'
,
target_details:
'target name'
,
entity_path:
'path'
,
from:
'a'
,
to:
'b'
}
end
let
(
:audit_event
)
{
create
(
:audit_event
,
details:
details
)
}
subject
(
:presenter
)
do
described_class
.
new
(
audit_event
)
end
it
'exposes the author name'
do
expect
(
presenter
.
author_name
).
to
eq
(
details
[
:author_name
])
end
it
'exposes the target'
do
expect
(
presenter
.
target
).
to
eq
(
details
[
:target_details
])
end
it
'exposes the ip address'
do
expect
(
presenter
.
ip_address
).
to
eq
(
details
[
:ip_address
])
end
it
'exposes the object'
do
expect
(
presenter
.
object
).
to
eq
(
details
[
:entity_path
])
end
it
'exposes the date'
do
expect
(
presenter
.
date
).
to
eq
(
audit_event
.
created_at
.
to_s
(
:db
))
end
it
'exposes the action'
do
expect
(
presenter
.
action
).
to
eq
(
'Changed author from a to b'
)
end
end
spec/services/audit_event_service_spec.rb
View file @
df01bc07
require
'spec_helper'
describe
AuditEventService
,
services:
true
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:project
)
{
create
(
:
empty_
project
)
}
let
(
:user
)
{
create
(
:user
)
}
let
(
:project_member
)
{
create
(
:project_member
,
user:
user
)
}
let
(
:service
)
{
described_class
.
new
(
user
,
project
,
{
action: :destroy
})
}
...
...
@@ -18,5 +18,15 @@ describe AuditEventService, services: true do
event
=
service
.
for_member
(
project_member
).
security_event
expect
(
event
[
:details
][
:target_details
]).
to
eq
(
'Deleted User'
)
end
it
'has the IP address'
do
event
=
service
.
for_member
(
project_member
).
security_event
expect
(
event
[
:details
][
:ip_address
]).
to
eq
(
user
.
current_sign_in_ip
)
end
it
'has the entity full path'
do
event
=
service
.
for_member
(
project_member
).
security_event
expect
(
event
[
:details
][
:entity_path
]).
to
eq
(
project
.
full_path
)
end
end
end
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