Skip to content
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

Improve security for queue settings #233

Merged
merged 8 commits into from
Mar 24, 2019
Merged

Conversation

james9909
Copy link
Member

The queue settings page can now only be viewed by course staff or queue admins, and will 403 otherwise.

I also noticed admissionControlUrl is not being filtered out on queue GET requests - is this something we want to hide from regular users?

@james9909 james9909 requested a review from nwalters512 March 14, 2019 01:53
@vercel
Copy link

vercel bot commented Mar 14, 2019

This pull request is automatically deployed with Now.
To access deployments, click Details below or on the icon next to each push.

Copy link
Contributor

@nwalters512 nwalters512 left a comment

Choose a reason for hiding this comment

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

Looks good, can you add a changelog entry?

@nwalters512
Copy link
Contributor

Thinking about how you chose to exclude the queue props, I think it'd be better to exclude it from the default Sequelize scope and then only explicitly include it for admins/course staff. That'll give us a sane default for whenever we have to read a queue out of the DB.

@james9909 james9909 changed the title Only allow course staff to view the settings page Improve security for queue settings Mar 14, 2019
@james9909
Copy link
Member Author

How would this work when we need the courseId for the queue to check for their permissions? Is there an easy way to add an attribute back to the model after it's already been queried? I don't think we should have to make two separate queries to accomplish this.
Also, would this also require us to query each queue with the correct scope in the "Get all open queues" endpoint?

@nwalters512
Copy link
Contributor

By this I mean we'd only exclude the private config (admission control) by default; everything else would still be in there. Scopes apply to queries of whole collections too, so nothing would change for the "get 1" vs "get N" cases.

@james9909
Copy link
Member Author

james9909 commented Mar 14, 2019

Regarding my "get N" question, my confusion was how we'd inject the private config for only the correct queues with one single query. Although I realize now that the queue home page doesn't need that kind of information anyways so it's not particularly important in the "Get all open queues" endpoint

The main thing I'm confused about is how we're supposed to apply the correct "admin" scope when making the queue query without already having queried the queue to see if the user is on course staff, which determines whether they're allowed to see the config or not. Is there another way to determine if a user is on course staff without querying the queue itself?

Something that I was considering to get around this while still implementing the scoping method is to have a separate "settings" endpoint that we'd only query in the settings page that will contain all that private information.

@nwalters512
Copy link
Contributor

Ah, I see what you're getting at now. Yeah, I would say for a list, it's fine if we just unconditionally omit them for now. Regarding checking for course staff, that would indeed be not ideal, but DB queries are also super cheap - most requests are already doing several.

I do like the idea of a settings endpoint - if we start adding more settings in the future, that'd be nice to only ever deliver them on the settings page. When I was first doing this, I actually thought about having a separate table for these sorts of settings, but ultimately decided that a 1-1 relationship like that felt weird. We could revisit that too.

Sequelize v5 fixes an issue with scopes not properly merging
attribute excludes when multiple scopes are in use.

See sequelize/sequelize#9087
src/api/queues.js Outdated Show resolved Hide resolved
src/api/queues.js Outdated Show resolved Hide resolved
return queue
})
const queuesResult = await Queue.scope(
'defaultScope',
Copy link
Contributor

Choose a reason for hiding this comment

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

Is specifying defaultScope explicitly required?

Copy link
Member Author

Choose a reason for hiding this comment

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

We need defaultScope so that the private attribute filtering gets applied, otherwise only questionCount will be used.

queueId,
askedById: askerId,
})
const question = await Question.create(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you clarify why there changes were necessary?

Copy link
Member Author

@james9909 james9909 Mar 18, 2019

Choose a reason for hiding this comment

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

We expect that the object returned from Question.build contains the appropriate askedBy model through association, but it doesn't do that anymore.
The docs suggest to use .create to create with association.

Copy link
Member Author

Choose a reason for hiding this comment

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

Looking through the docs some more, it's possible to use .build() to achieve the same effect, but since we're not doing anything special between .build() and .save(), I don't see a reason to use it over .create()

@@ -113,13 +113,25 @@ router.get(
},
required: false,
include: [User],
attributes: ['startTime', 'endTime'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you clarify why the changes in this file were necessary?

Copy link
Member Author

Choose a reason for hiding this comment

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

I can't find the exact change from v5, but without attributes, sequelize will throw an error stating include.attributes.map is not a function.

@@ -38,8 +38,6 @@ module.exports.initSequelize = sequelize => {
}

const sequelizeConfig = {
// See http://docs.sequelizejs.com/manual/querying.html#operators-security
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you clarify why this change was necessary?

Copy link
Member Author

@james9909 james9909 Mar 18, 2019

Choose a reason for hiding this comment

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

For security purposes, string-based operators ($or, $and, etc) have been deprecated and sequelize will now show deprecation warnings, even though we're not using string operators anywhere.
I was reading through sequelize/sequelize#8417 and it seems like the general consensus was to set operatorsAliases to false to completely disable string-based operators, but that will also trigger a warning as seen here.
Removing the option altogether has the same effect as setting it to false, but without the warning.

Copy link
Contributor

@nwalters512 nwalters512 left a comment

Choose a reason for hiding this comment

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

LGTM!

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