Commit 7a491a94 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'ee/master' into ce-to-ee-2017-11-03

* ee/master: (22 commits)
  Add project actions in Audit events
  Update EE-specific API helpers spec
  Pull API job token behavior in line with other tokens, and expand test coverage
  Update documentation
  Add changelog items
  Update specs for sudo behavior
  Move RSS and incoming email tokens from User Settings > Accounts to User Settings > Access Tokens
  Remove user authentication_token column
  Migrate user private tokens to personal access tokens
  Add sudo API scope
  Consistently use PersonalAccessToken instead of PersonalToken
  Remove User#private_token
  Remove authentication using user.private_token
  Remove tokens:reset_all_auth rake task
  Remove gitlab:users:clear_all_authentication_tokens rake task
  Remove private_token from API user entity
  Remove Session API
  Remove Private Token from User Settings > Account
  Update CHANGELOG.md for 10.1.1
  Update CHANGELOG-EE.md for 10.1.1-ee
  ...
parents 8dcff73e e40b800a
Please view this file on the master branch, on stable branches it's out of date.
## 10.1.1 (2017-10-31)
- No changes.
- [FIXED] Fix LDAP group sync for nested groups e.g. when base has uppercase or extraneous spaces. !3217
- [FIXED] Geo: read-only safeguards was not working on Secondary node. !3227
- [FIXED] fix height of rebase and approve buttons.
- [FIXED] Move group boards routes under - and remove "boards" from reserved paths.
## 10.1.0 (2017-10-22)
- [SECURITY] Prevent Related Issues from leaking confidential issues. !541
......
......@@ -2,6 +2,31 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 10.1.1 (2017-10-31)
- No changes.
- [FIXED] Auto Devops kubernetes default namespace is now correctly built out of gitlab project group-name. !14642 (Mircea Danila Dumitrescu)
- [FIXED] Forbid the usage of `Redis#keys`. !14889
- [FIXED] Make the circuitbreaker more robust by adding higher thresholds, and multiple access attempts. !14933
- [FIXED] Only cache last push event for existing projects when pushing to a fork. !14989
- [FIXED] Fix bug preventing secondary emails from being confirmed. !15010
- [FIXED] Fix broken wiki pages that link to a wiki file. !15019
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
- [FIXED] Fix bitbucket login. !15051
- [FIXED] Update gitaly in Gitlab 10.1 to 0.43.1 for temp file cleanup. !15055
- [FIXED] Use the correct visibility attribute for projects in system hooks. !15065
- [FIXED] Normalize LDAP DN when looking up identity.
- [FIXED] Adds callback functions for initial request in clusters page.
- [FIXED] Fix missing Import/Export issue assignees.
- [FIXED] Allow boards as top level route.
- [FIXED] Fix widget of locked merge requests not being presented.
- [FIXED] Fix editing issue description in mobile view.
- [FIXED] Fix deletion of container registry or images returning an error.
- [FIXED] Fix the writing of invalid environment refs.
- [CHANGED] Store circuitbreaker settings in the database instead of config. !14842
- [CHANGED] Update default disabled merge request widget message to reflect a general failure. !14960
- [PERFORMANCE] Stop merge requests with thousands of commits from timing out. !15063
## 10.1.0 (2017-10-22)
- [SECURITY] Use a timeout on certain git operations. !14872
......
class AuditEvent < ActiveRecord::Base
prepend EE::AuditEvent
serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize
belongs_to :user, foreign_key: :author_id
......@@ -9,15 +11,11 @@ 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 present
AuditEventPresenter.new(self)
def author_name
self.user.name
end
end
......@@ -3,19 +3,31 @@ AuditEventPresenter < Gitlab::View::Presenter::Simple
presents :audit_event
def author_name
audit_event.author_name || '(removed)'
user = audit_event.user
return nil unless user
link_to(user.name, user_path(user))
end
def target
audit_event.details[:target_details]
details[:target_details]
end
def ip_address
audit_event.details[:ip_address]
details[:ip_address]
end
def details
audit_event.details
end
def object
audit_event.details[:entity_path]
entity = audit_event.entity
return nil unless entity
link_to(details[:entity_path], entity).html_safe
end
def date
......@@ -23,6 +35,16 @@ AuditEventPresenter < Gitlab::View::Presenter::Simple
end
def action
Audit::Details.humanize(audit_event.details)
Audit::Details.humanize(details)
end
private
# The class can't include ActionView::Helpers::UrlHelper because it overwrites
# the method url_for. In this helper, that implementation of that method
# doesn't accept objects to resolve their route. That's why here we call the
# native url_for to get the route of the object and then call the link_to with it
def link_to(name, object)
ActionController::Base.helpers.link_to(name, url_for(object))
end
end
......@@ -20,7 +20,7 @@
- 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'} })
placeholder: admin_project_dropdown_label('Search projects'), idAttribute: 'id', data: { order_by: 'last_activity_at', idattribute: 'id', all_projects: 'true', simple_filter: 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',
......@@ -39,8 +39,16 @@
%tbody
- @events.map(&:present).each do |event|
%tr
%td= event.author_name
%td= event.object
%td
- if (author_link = event.author_name)
= author_link
- else
#{event.details[:author_name]} <small>(removed)</small>
%td
- if (object_link = event.object)
= object_link
- else
#{event.details[:entity_path]} <small>(removed)</small>
%td= event.action
%td= event.target
%td= event.ip_address
......
---
title: Add project actions in Audit events
merge_request: 3160
author:
type: changed
---
title: fix height of rebase and approve buttons
merge_request:
author:
type: fixed
---
title: 'Geo: read-only safeguards was not working on Secondary node'
merge_request: 3227
author:
type: fixed
---
title: Move group boards routes under - and remove "boards" from reserved paths
merge_request:
author:
type: fixed
---
title: Fix LDAP group sync for nested groups e.g. when base has uppercase or extraneous spaces
merge_request: 3217
author:
type: fixed
---
title: Update default disabled merge request widget message to reflect a general failure
merge_request: 14960
author:
type: changed
---
title: 'Fix bug preventing secondary emails from being confirmed'
merge_request: 15010
author:
type: fixed
---
title: Fix editing issue description in mobile view
merge_request:
author:
type: fixed
---
title: Fix bitbucket login
merge_request: 15051
author:
type: fixed
---
title: Make the circuitbreaker more robust by adding higher thresholds, and multiple
access attempts.
merge_request: 14933
author:
type: fixed
---
title: Store circuitbreaker settings in the database instead of config
merge_request: 14842
author:
type: changed
---
title: Forbid the usage of `Redis#keys`
merge_request: 14889
author:
type: fixed
---
title: Don't rename paths that were freed up when upgrading
merge_request: 15029
author:
type: fixed
---
title: Only cache last push event for existing projects when pushing to a fork
merge_request: 14989
author:
type: fixed
---
title: Use the correct visibility attribute for projects in system hooks
merge_request: 15065
author:
type: fixed
---
title: Fix broken wiki pages that link to a wiki file
merge_request: 15019
author:
type: fixed
---
title: Allow boards as top level route
merge_request:
author:
type: fixed
---
title: Auto Devops kubernetes default namespace is now correctly built out of gitlab
project group-name
merge_request: 14642
author: Mircea Danila Dumitrescu
type: fixed
---
title: Fix deletion of container registry or images returning an error
merge_request:
author:
type: fixed
---
title: Fix the writing of invalid environment refs
merge_request:
author:
type: fixed
module EE
module AuditEvent
extend ActiveSupport::Concern
def author_name
details[:author_name].blank? ? user&.name : details[:author_name]
end
def entity
return unless entity_type && entity_id
# Avoiding exception if the record doesn't exist
@entity ||= entity_type.constantize.find_by_id(entity_id)
end
def present
AuditEventPresenter.new(self)
end
end
end
......@@ -29,7 +29,7 @@ module EE
has_many :approvers, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :approver_groups, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :audit_events, as: :entity, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :audit_events, as: :entity
has_many :remote_mirrors, inverse_of: :project
has_many :path_locks
......
......@@ -63,8 +63,8 @@ module EE
to: @details[:to],
author_name: @author.name,
target_id: @entity.id,
target_type: @entity.class,
target_details: @entity.name
target_type: @entity.class.name,
target_details: @details[:target_details] || @entity.name
}
self
end
......@@ -93,6 +93,10 @@ module EE
)
end
def for_project
for_custom_model('project', @entity.full_path)
end
def entity_audit_events_enabled?
@entity.respond_to?(:feature_available?) && @entity.feature_available?(:audit_events)
end
......
......@@ -20,7 +20,10 @@ module EE
end
end
log_geo_event(project) if project&.persisted?
if project&.persisted?
log_geo_event(project)
log_audit_event(project)
end
project
end
......@@ -51,6 +54,14 @@ module EE
project.push_rule = push_rule
end
end
def log_audit_event(project)
::AuditEventService.new(
current_user,
project,
action: :create
).for_project.security_event
end
end
end
end
......@@ -9,6 +9,7 @@ module EE
if succeeded
mirror_cleanup(project)
log_geo_event(project)
log_audit_event(project)
end
succeeded
......@@ -45,6 +46,16 @@ module EE
::Geo::ProjectRegistry.where(project_id: project.id).delete_all
end
private
def log_audit_event(project)
::AuditEventService.new(
current_user,
project,
action: :destroy
).for_project.security_event
end
end
end
end
......@@ -8,6 +8,8 @@ module EE
super
EE::Audit::ProjectChangesAuditor.new(@current_user, project).execute
::Geo::RepositoryRenamedEventStore.new(
project,
old_path: project.path,
......
......@@ -10,7 +10,17 @@ module EE
params.delete(:mirror_trigger_builds)
end
super
result = super
log_audit_events if result[:status] == :success
result
end
private
def log_audit_events
EE::Audit::ProjectChangesAuditor.new(current_user, project).execute
end
end
end
......
......@@ -45,11 +45,7 @@ module API
# Helper Methods for Grape Endpoint
module HelperMethods
def find_current_user!
user =
find_user_from_access_token ||
find_user_from_warden ||
find_user_by_job_token
user = find_user_from_access_token || find_user_from_job_token || find_user_from_warden
return unless user
forbidden!('User is blocked') unless Gitlab::UserAccess.new(user).allowed? && user.can?(:access_api)
......@@ -84,6 +80,45 @@ module API
validate_access_token!
access_token.user || raise(UnauthorizedError)
@access_token = find_oauth_access_token || find_personal_access_token
end
def validate_access_token!(scopes: [])
return unless access_token
case AccessTokenValidationService.new(access_token, request: request).validate(scopes: scopes)
when AccessTokenValidationService::INSUFFICIENT_SCOPE
raise InsufficientScopeError.new(scopes)
when AccessTokenValidationService::EXPIRED
raise ExpiredError
when AccessTokenValidationService::REVOKED
raise RevokedError
end
end
private
def find_user_from_access_token
return unless access_token
validate_access_token!
access_token.user || raise(UnauthorizedError)
end
def find_user_from_job_token
return unless route_authentication_setting[:job_token_allowed]
token = (params[JOB_TOKEN_PARAM] || env[JOB_TOKEN_HEADER]).to_s
return unless token.present?
job = Ci::Build.find_by(token: token)
raise UnauthorizedError unless job
@job_token_authentication = true
job.user
end
# Check the Rails session for valid authentication details
......@@ -100,16 +135,6 @@ module API
Gitlab::RequestForgeryProtection.verified?(env)
end
def find_user_by_job_token
return @user_by_job_token if defined?(@user_by_job_token)
@user_by_job_token =
if route_authentication_setting[:job_token_allowed]
token_string = params[JOB_TOKEN_PARAM].presence || env[JOB_TOKEN_HEADER].presence
Ci::Build.find_by_token(token_string)&.user if token_string
end
end
def route_authentication_setting
return {} unless respond_to?(:route_setting)
......
......@@ -408,7 +408,7 @@ module API
end
def job_token_authentication?
initial_current_user && initial_current_user == find_user_by_job_token
initial_current_user && @job_token_authentication
end
def warden
......
module EE
module Audit
class BaseChangesAuditor
include Changes
def initialize(current_user, model)
@model = model
@current_user = current_user
end
def parse_options(column, options)
super.merge(attributes_from_auditable_model(column))
end
def attributes_from_auditable_model(column)
raise NotImplementedError
end
end
end
end
module EE
module Audit
class ProjectChangesAuditor < BaseChangesAuditor
def execute
audit_changes(:visibility_level, as: 'visibility', model: model)
audit_changes(:path, as: 'path', model: model)
audit_changes(:name, as: 'name', model: model)
audit_changes(:namespace_id, as: 'namespace', model: model)
end
def attributes_from_auditable_model(column)
case column
when :name
{
from: model.namespace.human_name + ' / ' + model.previous_changes[column].first.to_s,
to: model.full_name
}
when :path
{
from: model.old_path_with_namespace.to_s,
to: model.full_path
}
when :visibility_level
{
from: ::Gitlab::VisibilityLevel.level_name(model.previous_changes[column].first),
to: ::Gitlab::VisibilityLevel.level_name(model.previous_changes[column].last)
}
when :namespace_id
{
from: model.old_path_with_namespace,
to: model.full_path
}
end.merge(target_details: model.full_path)
end
end
end
end
require 'spec_helper'
describe EE::API::Helpers do
let(:env) { { 'rack.input' => StringIO.new } }
let(:helper) do
Class.new { include API::Helpers, API::APIGuard::HelperMethods }.new
include API::APIGuard::HelperMethods
include API::Helpers
let(:options) { {} }
let(:params) { {} }
let(:env) do
{
'rack.input' => '',
'REQUEST_METHOD' => 'GET'
}
end
let(:header) { }
before do
allow(helper).to receive(:env).and_return(env)
allow(helper).to receive(:params).and_return({})
allow(helper).to receive(:options).and_return({})
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
end
describe '#current_user' do
let(:user) { build(:user, id: 42) }
before do
allow(helper).to receive(:sudo!)
end
it 'handles sticking when a user could be found' do
allow(helper).to receive(:initial_current_user).and_return(user)
allow_any_instance_of(API::Helpers).to receive(:initial_current_user).and_return(user)
expect(Gitlab::Database::LoadBalancing::RackMiddleware)
.to receive(:stick_or_unstick).with(env, :user, 42)
helper.current_user
current_user
end
it 'does not handle sticking if no user could be found' do
allow(helper).to receive(:initial_current_user).and_return(nil)
allow_any_instance_of(API::Helpers).to receive(:initial_current_user).and_return(nil)
expect(Gitlab::Database::LoadBalancing::RackMiddleware)
.not_to receive(:stick_or_unstick)
helper.current_user
current_user
end
it 'returns the user if one could be found' do
allow(helper).to receive(:initial_current_user).and_return(user)
allow_any_instance_of(API::Helpers).to receive(:initial_current_user).and_return(user)
expect(helper.current_user).to eq(user)
expect(current_user).to eq(user)
end
end
end
......@@ -16,6 +16,7 @@ describe Project do
it { is_expected.to have_many(:path_locks) }
it { is_expected.to have_many(:sourced_pipelines) }
it { is_expected.to have_many(:source_pipelines) }
it { is_expected.to have_many(:audit_events) }
end
describe '#push_rule' do
......
......@@ -32,4 +32,71 @@ describe AuditEventService do
expect(event.details[:failed_login]).to eq('LDAP')
end
end
describe 'license' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let!(:service) { described_class.new(user, project, action: :create) }
let(:event) { service.for_project.security_event }
before do
disable_license_audit_features(service)
end
describe 'has the audit_admin feature' do
before do
allow(service).to receive(:admin_audit_log_enabled?).and_return(true)
end
it 'logs an audit event' do
expect { event }.to change(AuditEvent, :count).by(1)
end
it 'has the entity_path' do
expect(event.details[:entity_path]).to eq(project.full_path)
end
end
describe 'has the extended_audit_events feature' do
before do
allow(service).to receive(:entity_audit_events_enabled?).and_return(true)
end
it 'logs an audit event' do
expect { event }.to change(AuditEvent, :count).by(1)
end
it 'has not the entity_path' do
expect(event.details[:entity_path]).not_to eq(project.full_path)
end
end
describe 'entity has the audit_events feature' do
before do
allow(service).to receive(:audit_events_enabled?).and_return(true)
end
it 'logs an audit event' do
expect { event }.to change(AuditEvent, :count).by(1)
end
it 'has not the entity_path' do
expect(event.details[:entity_path]).not_to eq(project.full_path)
end
end
describe 'has not any audit event feature' do
it 'does not log the audit event' do
expect { event }.not_to change(AuditEvent, :count)
end
end
def disable_license_audit_features(service)
[:entity_audit_events_enabled?,
:admin_audit_log_enabled?,
:audit_events_enabled?].each do |f|
allow(service).to receive(f).and_return(false)
end
end
end
end
......@@ -202,6 +202,29 @@ describe Projects::CreateService, '#execute' do
end
end
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { create_project(user, opts) }
let(:fail_condition!) do
allow(Gitlab::VisibilityLevel).to receive(:allowed_for?).and_return(false)
end
let(:attributes) do
{
author_id: user.id,
entity_id: @resource.id,
entity_type: 'Project',
details: {
add: 'project',
author_name: user.name,
target_id: @resource.full_path,
target_type: 'Project',
target_details: @resource.full_path
}
}
end
end
end
def create_project(user, opts)
described_class.new(user, opts).execute
end
......
......@@ -49,4 +49,28 @@ describe Projects::DestroyService do
end
end
end
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { subject.execute }
let(:fail_condition!) do
expect_any_instance_of(Project)
.to receive(:destroy!).and_raise(StandardError.new('Other error message'))
end
let(:attributes) do
{
author_id: user.id,
entity_id: project.id,
entity_type: 'Project',
details: {
remove: 'project',
author_name: user.name,
target_id: project.full_path,
target_type: 'Project',
target_details: project.full_path
}
}
end
end
end
end
......@@ -18,4 +18,30 @@ describe Projects::TransferService do
expect { subject.execute(group) }.to change(Geo::RepositoryRenamedEvent, :count).by(1)
end
end
context 'audit events' do
include_examples 'audit event logging' do
let(:operation) { subject.execute(group) }
let(:fail_condition!) do
expect_any_instance_of(Project)
.to receive(:has_container_registry_tags?).and_return(true)
end
let(:attributes) do
{
author_id: user.id,
entity_id: project.id,
entity_type: 'Project',
details: {
change: 'namespace',
from: project.old_path_with_namespace,
to: project.full_path,
author_name: user.name,
target_id: project.id,
target_type: 'Project',
target_details: project.full_path
}
}
end
end
end
end
......@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::UpdateService, '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project, creator: user, namespace: user.namespace) }
let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
context 'repository mirror' do
let!(:opts) do
......@@ -47,6 +47,82 @@ describe Projects::UpdateService, '#execute' do
end
end
context 'audit events' do
let(:audit_event_params) do
{
author_id: user.id,
entity_id: project.id,
entity_type: 'Project',
details: {
author_name: user.name,
target_id: project.id,
target_type: 'Project',
target_details: project.full_path
}
}
end
context '#name' do
include_examples 'audit event logging' do
let!(:old_name) { project.full_name }
let(:operation) { update_project(project, user, name: 'foobar') }
let(:fail_condition!) do
allow_any_instance_of(Project).to receive(:update_attributes).and_return(false)
end
let(:attributes) do
audit_event_params.tap do |param|
param[:details].merge!(
change: 'name',
from: old_name,
to: project.full_name
)
end
end
end
end
context '#path' do
include_examples 'audit event logging' do
let(:operation) { update_project(project, user, path: 'foobar1') }
let(:fail_condition!) do
allow_any_instance_of(Project).to receive(:update_attributes).and_return(false)
end
let(:attributes) do
audit_event_params.tap do |param|
param[:details].merge!(
change: 'path',
from: project.old_path_with_namespace,
to: project.full_path
)
end
end
end
end
context '#visibility' do
include_examples 'audit event logging' do
let(:operation) do
update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
let(:fail_condition!) do
allow_any_instance_of(Project).to receive(:update_attributes).and_return(false)
end
let(:attributes) do
audit_event_params.tap do |param|
param[:details].merge!(
change: 'visibility',
from: 'Private',
to: 'Internal'
)
end
end
end
end
end
def update_project(project, user, opts)
Projects::UpdateService.new(project, user, opts).execute
end
......
......@@ -15,7 +15,9 @@ describe Audit::Details do
end
it 'humanizes user login action' do
expect(described_class.humanize(login_action)).to eq('Signed in with LDAP authentication')
string = described_class.humanize(login_action)
expect(string).to eq('Signed in with LDAP authentication')
end
end
......@@ -35,7 +37,9 @@ describe Audit::Details do
end
it 'humanizes add project member access action' do
expect(described_class.humanize(member_access_action)).to eq('Added user access as Developer')
string = described_class.humanize(member_access_action)
expect(string).to eq('Added user access as Developer')
end
end
......@@ -56,7 +60,9 @@ describe Audit::Details do
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')
string = described_class.humanize(member_access_action)
expect(string).to eq('Changed access level from Guest to Owner')
end
end
......@@ -72,7 +78,9 @@ describe Audit::Details do
end
it 'humanizes the removal action' do
expect(described_class.humanize(removal_action)).to eq('Removed deploy key')
string = described_class.humanize(removal_action)
expect(string).to eq('Removed deploy key')
end
end
......@@ -90,7 +98,9 @@ describe Audit::Details do
end
it 'humanizes the removal action' do
expect(described_class.humanize(action)).to eq('Changed email from a@b.com to c@b.com')
string = described_class.humanize(action)
expect(string).to eq('Changed email from a@b.com to c@b.com')
end
end
end
......
require 'spec_helper'
describe EE::Audit::ProjectChangesAuditor do
describe '.audit_changes' do
let!(:user) { create(:user) }
let!(:project) { create(:project, visibility_level: 0) }
let(:foo_instance) { described_class.new(user, project) }
before do
stub_licensed_features(extended_audit_events: true)
end
describe 'non audit changes' do
it 'does not call the audit event service' do
project.update!(description: 'new description')
expect { foo_instance.execute }.not_to change { SecurityEvent.count }
end
end
describe 'audit changes' do
it 'creates an event when the visibility change' do
project.update!(visibility_level: 20)
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
expect(SecurityEvent.last.details[:change]).to eq 'visibility'
end
it 'creates an event when the name change' do
project.update!(name: 'new name')
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
expect(SecurityEvent.last.details[:change]).to eq 'name'
end
it 'creates an event when the path change' do
project.update!(path: 'newpath')
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
expect(SecurityEvent.last.details[:change]).to eq 'path'
end
it 'creates an event when the namespace change' do
new_namespace = create(:namespace)
project.update!(namespace: new_namespace)
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
expect(SecurityEvent.last.details[:change]).to eq 'namespace'
end
end
end
end
......@@ -43,6 +43,26 @@ RSpec.describe AuditEvent, type: :model do
end
end
describe '#entity' do
context 'when entity exists' do
let(:user) { create(:user, name: 'John Doe') }
subject(:event) { described_class.new(entity_id: user.id, entity_type: user.class.name) }
it 'returns the entity object' do
expect(event.entity).to eq user
end
end
context 'when entity does not exist' do
subject(:event) { described_class.new(entity_id: 99999, entity_type: 'User') }
it 'returns nil' do
expect(event.entity).to be_blank
end
end
end
describe '#present' do
it 'returns a presenter' do
expect(subject.present).to be_an_instance_of(AuditEventPresenter)
......
require 'spec_helper'
describe AuditEventPresenter do
include Gitlab::Routing.url_helpers
let(:details) do
{
author_name: 'author',
......@@ -18,8 +20,16 @@ describe AuditEventPresenter do
described_class.new(audit_event)
end
it 'exposes the author name' do
expect(presenter.author_name).to eq(details[:author_name])
context 'exposes the author' do
it 'shows a link if it exists' do
expect(presenter.author_name).to eq("<a href=\"#{user_path(audit_event.user)}\">#{audit_event.user.name}</a>")
end
it 'stores the name if it has been deleted' do
audit_event.user = nil
expect(presenter.author_name).to be_blank
end
end
it 'exposes the target' do
......@@ -30,8 +40,16 @@ describe AuditEventPresenter do
expect(presenter.ip_address).to eq(details[:ip_address])
end
it 'exposes the object' do
expect(presenter.object).to eq(details[:entity_path])
context 'exposes the object' do
it 'link if it exists' do
expect(presenter.object).to eq("<a href=\"#{url_for(audit_event.entity)}\">#{details[:entity_path]}</a>")
end
it 'stored name if it has been deleted' do
audit_event.entity_id = nil
expect(presenter.object).to be_blank
end
end
it 'exposes the date' do
......
......@@ -182,31 +182,47 @@ describe API::Helpers do
end
describe "when authenticating using a job token" do
let(:job) { create(:ci_build, user: current_user) }
let(:route_authentication_setting) { { job_token_allowed: true } }
let(:job) { create(:ci_build, user: user) }
before do
allow_any_instance_of(described_class).to receive(:doorkeeper_guard).and_return(nil)
end
context 'when route is allowed to be authenticated' do
let(:route_authentication_setting) { { job_token_allowed: true } }
it "returns nil for an invalid token" do
env[API::APIGuard::JOB_TOKEN_HEADER] = 'invalid token'
it "returns a 401 response for an invalid token" do
env[API::APIGuard::JOB_TOKEN_HEADER] = 'invalid token'
expect(current_user).to be_nil
end
expect { current_user }.to raise_error /401/
end
it "returns nil for a user without access" do
env[API::APIGuard::JOB_TOKEN_HEADER] = job.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
it "returns a 403 response for a user without access" do
env[API::APIGuard::JOB_TOKEN_HEADER] = job.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect { current_user }.to raise_error /403/
end
expect(current_user).to be_nil
it 'returns a 403 response for a user who is blocked' do
user.block!
env[API::APIGuard::JOB_TOKEN_HEADER] = job.token
expect { current_user }.to raise_error /403/
end
it "sets current_user" do
env[API::APIGuard::JOB_TOKEN_HEADER] = job.token
expect(current_user).to eq(user)
end
end
it "returns nil for a user with access, but route not allowed to be authenticated" do
env[API::APIGuard::JOB_TOKEN_HEADER] = job.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
context 'when route is not allowed to be authenticated' do
let(:route_authentication_setting) { { job_token_allowed: false } }
it "sets current_user to nil" do
env[API::APIGuard::JOB_TOKEN_HEADER] = job.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
expect(current_user).to be_nil
expect(current_user).to be_nil
end
end
end
end
......
shared_examples_for 'audit event logging' do
before do
stub_licensed_features(extended_audit_events: true)
end
context 'if operation succeed' do
it 'logs an audit event if operation succeed' do
expect { operation }.to change(AuditEvent, :count).by(1)
end
it 'logs the project info' do
@resource = operation
expect(AuditEvent.last).to have_attributes(attributes)
end
end
it 'does not log audit event if project operation fails' do
fail_condition!
expect { operation }.not_to change(AuditEvent, :count)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment