-
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optional Feature: require admin for prod (un)lock. #2492
Optional Feature: require admin for prod (un)lock. #2492
Conversation
b7eb192
to
c487df7
Compare
Build failure is unrelated to the changes for this pull request. Fails on slow test (didn't see this when I ran it locally)
And coverage:
|
hmmm I kinda like the idea of only letting admins unlock ... do you think the same restriction is necessary for locking ? ... I'd rather have anyone able to lock when something goes wrong ... |
.env.example
Outdated
@@ -173,3 +173,6 @@ PERIODICAL=stop_expired_deploys:60,remove_expired_locks:60,report_system_stats:6 | |||
# GCLOUD_OPTIONS=--verbose # optional, options | |||
# GCLOUD_PROJECT=foo-123 | |||
# GCLOUD_ACCOUNT=my-account | |||
|
|||
## Feature: Only admins can (un)lock stages which affect production. | |||
# FEATURE_ONLY_ADMINS_CAN_LOCK_UNLOCK_PRODUCTION_STAGE=1 |
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.
ONLY_ADMINS_CAN_UNLOCK_PRODUCTION_STAGE
should be long enough :D
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.
... maybe PRODUCTION_STAGE_LOCK_REQUIRES_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.
yeah, naming things is always hard ;-) I like PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN.
app/controllers/locks_controller.rb
Outdated
@@ -60,11 +60,25 @@ def lock | |||
def require_project | |||
case action_name | |||
when 'create' then | |||
@project = Stage.find(params[:lock][:resource_id]).project | |||
stage = Stage.find(params[:lock][:resource_id]) |
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.
this should only be handles by access_control.rb
and not copied here ...
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.
I agree with this, but it seems that only the project object is passed into access control to make the decision for the locks. I struggled with my debugger configuration, so maybe I overlooked something... but this is the reason for the hacking singleton method below. I was basically trying to attach the stage attribute information to the project. I'll do some more investigating...
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.
So I have a question about this:
# app/controllers/locks_controller.rb line 6
before_action :require_project, if: :for_stage_lock?
I was going to originally change this to
before_action :require_stage, if: :for_stage_lock?
Is there some reason that could not be done? Are there other use cases that use a Project
to determine lockability? It seems the locks apply to 2 things, global, and Stages
. Why is it that we pass a Project
in for the access check?
app/controllers/locks_controller.rb
Outdated
when 'destroy' then | ||
@project = lock.resource.project | ||
if ENV['FEATURE_ONLY_ADMINS_CAN_LOCK_UNLOCK_PRODUCTION_STAGE'] && lock.resource.production | ||
@project.define_singleton_method(:production) { true } |
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.
this looks very wrong :D
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.
Yeah, see my other comment above.
I think it's just because admin_for etc need a project, should be alright
to pass the stage in
…On Thu, Jan 25, 2018 at 8:48 AM, Jesse Goerz ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In app/controllers/locks_controller.rb
<#2492 (comment)>:
> @@ -60,11 +60,25 @@ def lock
def require_project
case action_name
when 'create' then
- @project = Stage.find(params[:lock][:resource_id]).project
+ stage = Stage.find(params[:lock][:resource_id])
So I have a question about this:
# app/controllers/locks_controller.rb line 6
before_action :require_project, if: :for_stage_lock?
I was going to originally change this to
before_action :require_stage, if: :for_stage_lock?
Is there some reason that could not be done? Are there other use cases
that use a Project to determine lockability? It seems the locks apply to
2 things, global, and Stages. Why is it that we pass a Project in for the
access check?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#2492 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAAsZ1HHOo1Z8T2rCftKlAbrKoczBfB8ks5tOLBDgaJpZM4Rr85i>
.
|
The below is going to fail on the "locks" current_user test (line 245: "renders when authorized via the project"). I'm a little confused by this test. It seems to be testing if a project can be viewed by checking project level authorization... but a lock is only accessed after viewing the project/stage so I don't believe this is a valid test for the locks controller. Let me know what you think. |
For our use case, we just want to prevent accidental production deploys. We plan on locking production stages with non-expiring locks. So letting anyone lock wouldn't be a problem for us. I could see how someone would want to strictly control both (un)lock. The only thing I could see a problem with, is that after someone locked it, because of the role/perms, the Unlock button would be grayed out or not displayed at all... which might be confusing if someone was just pushing buttons (exploring/learning). If it is implemented... maybe split the lock actions into
If I had a choice, I'd choose to leave it as is (less work for me :-P ). But I'm flexible. |
649f8e3
to
22aeacf
Compare
protected | ||
|
||
def require_stage | ||
@stage = (Stage.find_by_param!(params[:stage_id]) if params[:stage_id]) |
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.
this won't work since stage permalinks are not globally unique, need to find base on project
@@ -66,7 +66,12 @@ def resource_action | |||
end | |||
|
|||
def can?(action, resource_namespace, scope = nil) | |||
scope ||= current_project if respond_to?(:current_project) | |||
case resource_namespace |
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.
this should be scope ||=
to avoid duplication and to avoid the respond_to?
calls
app/models/access_control.rb
Outdated
user.admin? # global locks | ||
elsif ENV['PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN'] && scope.production |
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.
might be easier to read as:
else # stage locks
if PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN
else
end
end
perform_get(project_id: projects(:test).id) | ||
# This still authorizes based on Project, but for the lock controller, it needs a Stage object | ||
# passed to access control since locks are per Stage. See app/models/access_control.rb lines | ||
# 22 - 28. |
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.
lines change ... prefer names :)
22aeacf
to
31bc90b
Compare
Ready for review/feedback. |
- When set, will require admin role for (un)locking stages with production attribute set. - All other locking is unchanged.
c9c8982
to
183e702
Compare
@@ -66,7 +66,11 @@ def resource_action | |||
end | |||
|
|||
def can?(action, resource_namespace, scope = nil) | |||
scope ||= current_project if respond_to?(:current_project) | |||
scope ||= if resource_namespace == :locks | |||
try(:current_stage) |
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 like nil.try(:foo?)
app/models/access_control.rb
Outdated
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
prefer to guard this in a elsif scope.is_a?(Stage)
so future additions don't require a refactor
@@ -118,6 +119,33 @@ def create_lock(resource = nil, options = {}) | |||
end | |||
end | |||
|
|||
describe '#create with PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN' do | |||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
remove the before and after ... they are no necessary ?
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
if it's not too complicated I'd prefer this under #create
as describe "with PRODUCTION_STAGE_LOCK_REQUIRES_ADMIN"
@@ -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 comment
The 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 ...
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
same here
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 comment
The reason will be displayed to describe this comment to others. Learn more.
stage.update_column(:production, true)
stage.save | ||
delete :destroy, params: {id: lock.id} | ||
|
||
assert_response :unauthorized |
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.
this might be enough and drop the later assertions ?
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 comment
The reason will be displayed to describe this comment to others. Learn more.
can drop the later assertions ... same in the test below
Ready for review/feedback. |
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.
awesome 🎉
/cc @zendesk/samson
Tasks
References
Risks