-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional Feature: require admin for prod (un)lock. #2492
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
module CurrentStage | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
before_action :require_stage | ||
helper_method :current_stage | ||
end | ||
|
||
def current_stage | ||
@stage | ||
end | ||
|
||
protected | ||
|
||
def require_stage | ||
if current_project | ||
query = Stage.where(project_id: current_project.id) | ||
stage_param = params[:stage_id] || params[:id] | ||
@stage = query.find_by_param!(stage_param) if stage_param | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,11 +18,15 @@ def can?(user, action, resource_namespace, scope = nil) | |
case action | ||
when :read then true | ||
when :write | ||
if scope | ||
user.deployer_for?(scope) # stage locks | ||
|
||
if scope.nil? # global locks | ||
user.admin? | ||
elsif ENV['PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN'] && scope.production | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might be easier to read as:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prefer to guard this in a |
||
user.admin_for?(scope.project) # stage locks | ||
else | ||
user.admin? # global locks | ||
user.deployer_for?(scope.project) # stage locks | ||
end | ||
|
||
else raise ArgumentError, "Unsupported action #{action}" | ||
end | ||
when :projects, :build_commands, :stages, :user_project_roles | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
require_relative '../../test_helper' | ||
|
||
SingleCov.covered! | ||
|
||
class CurrentStageConcernTest < ActionController::TestCase | ||
class CurrentStageTestController < ApplicationController | ||
include CurrentProject | ||
include CurrentStage | ||
|
||
def show | ||
render inline: '<%= current_stage.class.name %>' | ||
end | ||
end | ||
|
||
tests CurrentStageTestController | ||
use_test_routes CurrentStageTestController | ||
|
||
let(:project) { projects(:test) } | ||
let(:stage) { stages(:test_staging) } | ||
|
||
before { login_as(users(:viewer)) } | ||
|
||
it "finds current stage" do | ||
get :show, params: {project_id: project.id, id: stage.id, test_route: true} | ||
response.body.must_equal 'Stage' | ||
end | ||
|
||
it "fails with invalid stage id" do | ||
assert_raises ActiveRecord::RecordNotFound do | ||
get :show, params: {project_id: project.id, id: 123456, test_route: true} | ||
end | ||
end | ||
|
||
it "does not fail without stage" do | ||
get :show, params: {test_route: true} | ||
response.body.must_equal 'NilClass' | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ def create_lock(resource = nil, options = {}) | |
end | ||
|
||
let(:stage) { stages(:test_staging) } | ||
let(:prod_stage) { stages(:test_production) } | ||
let(:environment) { environments(:production) } | ||
let(:lock) { stage.create_lock! user: users(:deployer) } | ||
let(:global_lock) { Lock.create! user: users(:deployer) } | ||
|
@@ -27,11 +28,11 @@ def create_lock(resource = nil, options = {}) | |
end | ||
end | ||
|
||
describe "#require_project" do | ||
describe "#require_stage" do | ||
it "raises on unsupported action" do | ||
@controller.stubs(action_name: 'show') | ||
assert_raises RuntimeError do | ||
@controller.send(:require_project) | ||
@controller.send(:require_stage) | ||
end | ||
end | ||
end | ||
|
@@ -118,6 +119,33 @@ def create_lock(resource = nil, options = {}) | |
end | ||
end | ||
|
||
describe '#create with PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if it's not too complicated I'd prefer this under |
||
with_env 'PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' => 'true' | ||
before { travel_to Time.now } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove the before and after ... they are no necessary ? |
||
after { travel_back } | ||
|
||
it 'cannot create a stage lock for a production stage' do | ||
create_lock prod_stage, delete_in: 3600 | ||
|
||
assert_response :unauthorized | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can drop the later assertions ... same in the test below |
||
stage.reload | ||
assert_nil(stage.lock) | ||
end | ||
|
||
it 'creates a stage lock' do | ||
create_lock stage, delete_in: 3600 | ||
assert_redirected_to '/back' | ||
assert flash[:notice] | ||
|
||
stage.reload | ||
|
||
lock = stage.lock | ||
lock.warning?.must_equal(false) | ||
lock.description.must_equal 'DESC' | ||
lock.delete_at.must_equal(Time.now + 3600) | ||
end | ||
end | ||
|
||
describe '#destroy' do | ||
let(:lock) { stage.create_lock!(user: users(:deployer)) } | ||
|
||
|
@@ -136,6 +164,31 @@ def create_lock(resource = nil, options = {}) | |
Lock.count.must_equal 0 | ||
end | ||
end | ||
|
||
describe '#destroy with PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' do | ||
with_env 'PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' => 'true' | ||
|
||
it 'no change in default behavior for non-production stage lock' do | ||
delete :destroy, params: {id: lock.id} | ||
|
||
assert_redirected_to '/back' | ||
assert flash[:notice] | ||
|
||
Lock.count.must_equal 0 | ||
end | ||
|
||
it 'cannot destroy a stage production lock' do | ||
stage.production = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
stage.save | ||
delete :destroy, params: {id: lock.id} | ||
|
||
assert_response :unauthorized | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this might be enough and drop the later assertions ? |
||
stage.reload | ||
Lock.count.must_equal 1 | ||
stage.production = false | ||
stage.save | ||
end | ||
end | ||
end | ||
|
||
as_an_admin do | ||
|
@@ -159,6 +212,48 @@ def create_lock(resource = nil, options = {}) | |
end | ||
end | ||
|
||
describe '#create with PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. too much copy-paste for my taste ... the tests above should be sufficient ... |
||
with_env 'PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' => 'true' | ||
before { travel_to Time.now } | ||
after { travel_back } | ||
|
||
it 'creates a stage lock for a production stage' do | ||
stage.production = true | ||
stage.save | ||
create_lock stage, delete_in: 3600 | ||
|
||
assert_redirected_to '/back' | ||
assert flash[:notice] | ||
|
||
stage.reload | ||
|
||
lock = stage.lock | ||
lock.warning?.must_equal(false) | ||
lock.description.must_equal 'DESC' | ||
lock.delete_at.must_equal(Time.now + 3600) | ||
stage.production = false | ||
stage.save | ||
end | ||
|
||
it 'creates a global lock' do | ||
create_lock | ||
assert_redirected_to '/back' | ||
assert flash[:notice] | ||
|
||
lock = Lock.global.first | ||
lock.description.must_equal 'DESC' | ||
end | ||
|
||
it 'creates an environment lock' do | ||
create_lock environment | ||
assert_redirected_to '/back' | ||
assert flash[:notice] | ||
|
||
lock = environment.lock | ||
lock.description.must_equal 'DESC' | ||
end | ||
end | ||
|
||
describe '#destroy' do | ||
it 'destroys a global lock' do | ||
delete :destroy, params: {id: global_lock.id} | ||
|
@@ -170,6 +265,19 @@ def create_lock(resource = nil, options = {}) | |
end | ||
end | ||
|
||
describe '#destroy with PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
with_env 'PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' => 'true' | ||
|
||
it 'destroys a global lock' do | ||
delete :destroy, params: {id: global_lock.id} | ||
|
||
assert_redirected_to '/back' | ||
assert flash[:notice] | ||
|
||
Lock.count.must_equal 0 | ||
end | ||
end | ||
|
||
describe "#destroy_via_resource" do | ||
before { Lock.create!(user: users(:admin)) } | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try
does not make sense here, try is for nil prevention likenil.try(:foo?)