Skip to content

Teach 1975/ensure in development units are not visible to signed out users #66459

Merged
alex-m-brown merged 14 commits into
stagingfrom
TEACH-1975/Ensure-in-development-units-are-not-visible-to-signed-out-users-
Jun 18, 2025
Merged

Teach 1975/ensure in development units are not visible to signed out users #66459
alex-m-brown merged 14 commits into
stagingfrom
TEACH-1975/Ensure-in-development-units-are-not-visible-to-signed-out-users-

Conversation

@alex-m-brown

@alex-m-brown alex-m-brown commented Jun 11, 2025

Copy link
Copy Markdown
Contributor

Currently, with the new /courses/.../units/... URL structure, units were not being authorized correctly with authorize_resource. This PR adds a separate before action to manually authorize the selected unit.

Background

I found that the issue came down to CanCan authorizing the Unit model instead of the specific unit object with the new url structure. Based on my understanding, this is because the new urls no longer have the :id parameter to search for the unit by, which CanCan falls back to.

@script is set in :set_unit. The instance_name part of authorize_resource is telling it to reference the @script instance when authorizing. Through debugging I confirmed that @script was properly set when authorize_resource was called, it just wasn't being used to authorize. It wasn't until I added an id_param that it actually used @script. From my understanding, id_param tells CanCan what parameter to use to find the unit if @script is empty when it tries to authorize. With the new url structure, we don't get a param that could be used by that find method to identify a Unit.

Note: db_query_test.rb had to be updated because prior to this PR, the unit was rendering a no_access instead of the unit overview page. the changes in this PR have the side-effect of fixing the test, resulting in a larger query count.

Links

This solves 2 tickets:

Testing story

  • Added unit tests

@alex-m-brown alex-m-brown marked this pull request as ready for review June 11, 2025 22:16
# TEACH-1975 for more details).This solves the issue by explicitly calling authorize!
# on the @script if it is set.
private def authorize_script
authorize! params[:action].to_sym, @script || Unit

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I just ran into a similar issue on my UI test PR, which involves some similar logic in the script levels controller. I don't see how this can work in all cases because cancan doesn't know about the published state of the UnitGroup, which is (should be?) a factor in whether the unit is visible to a user via show.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please make sure you are testing these scenarios or something equivalent?

  • original unit group is in_development, modular unit is in beta, student CAN access /courses/modular/units/1
  • original unit group is in beta, modular unit is in_development, student can NOT access /courses/modular/units/1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, yeah, just testing manually the first thing i found which was csd-2025 in preview as the original course and focus-on-coding-2025 which is in development as the "modular" course. I was able to access courses/focus-on-coding-2025/units/1. It looks like we'll need to add an additional check on the unit group.

@alex-m-brown alex-m-brown Jun 12, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts on authorizing the unit group in that same method?

  private def authorize_script
    authorize! params[:action].to_sym, @course || UnitGroup
    authorize! params[:action].to_sym, @script || Unit
  end

@alex-m-brown alex-m-brown Jun 12, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind. that only works when the modular course is "stricter" than the original course, for example, the original course is stable, but the modular course is in development.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got as far as being not quite sure how to approach this -- I'm not sure cancan is well set up to authorize when multiple objects are needed. Will dig in more this morning.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use other auth methods outside of can can is that's what's needed. Not ideal, however, everything should be as simple as possible but not simpler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts on authorizing the unit group in that same method?

  private def authorize_script
    authorize! params[:action].to_sym, @course || UnitGroup
    authorize! params[:action].to_sym, @script || Unit
  end

I think you are on the right track here --

  • authorize UnitGroup based on published state, etc
  • authorize Unit based on login_required but NOT published state (of unit or of original unit group)

thoughts?

@davidsbailey davidsbailey left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution looks great! One readability nitpick inline.

else
false
end
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

Comment on lines +1596 to +1598
test_user_gets_response_for :show, response: :not_found, user: nil,
params: -> {{course_course_name: original_course.name, unit_position: unit_position, position: lesson_position}},
name: 'signed out user cannot view in-development original course'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is technically against any style rule, but I'm finding this formatting to be a bit hard to read, at least with github wrapping it at around 100 chars. in particular it's hard to quickly see the name or the difference in params. Would this style work for you?

test_user_gets_response_for(
:index,
user: user_type,
response: response,
params: {course: Pd::Workshop::COURSES.first}
)

or this (from later in this PR)?

test_user_gets_response_for(:show, response: :success, user: :teacher,
params: -> {{course_course_name: @pilot_course.name, position: 1}},
name: 'teacher without pilot access cannot view pilot unit'
) do

as an alternative, I could also see extracting original_path and modular_path param objects that could be passed in as params: -> {original_path} to make the lines a bit shorter.

sorry for the nit 😬

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No apologies, I agree! I'll update it to make it more readable. thanks!

@alex-m-brown alex-m-brown merged commit 28387ac into staging Jun 18, 2025
6 checks passed
@alex-m-brown alex-m-brown deleted the TEACH-1975/Ensure-in-development-units-are-not-visible-to-signed-out-users- branch June 18, 2025 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants