-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Address issue #323 and enable GHE Auth #491
Conversation
@tortilaman fyi I rebased your branch so that only the new commits are included in this PR. |
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.
Just one small change, otherwise LGTM! Thanks!
.eslintrc
Outdated
@@ -11,5 +11,10 @@ | |||
}, | |||
"globals": { | |||
"CMS_ENV": true | |||
}, | |||
"parserOptions": { |
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 this for now, can be brought up in a separate PR.
Yeah, I was gonna ask about rebasing. Figured that would be a good idea. I've actually now modified to provide better feedback so it uses the existing toast UI and redux-notifications. I feel comfortable merging at this point. |
Apologies for the double commit there, was using github desktop instead of cli and sometimes it doesn't make it clear what it's doing. |
Fixed history again - if you do a hard reset to origin from your local repo, that should prevent further extraneous commits. |
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.
Last thing, just updates to the error messages.
user.token = state.token; | ||
return user; | ||
} | ||
throw new Error('No response from Github, that\'s odd.'); |
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.
Replace with "GitHub is not responding, please try again."
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.
That probably makes more sense, haha. Leftover from my initial testing
this.api.collaborator(user.login).then((status) => { | ||
if (status === 404 || status === 403) { | ||
// Unauthorized user | ||
throw new Error("Sorry, you don't have CMS access."); |
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.
Replace with "Your GitHub user account does not have access to this repo."
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 was thinking that it might be beneficial in terms of users' mental model to abstract it a bit from GitHub since I'm sure there's a lot of end-users doing editing and writing that might not necessarily be so familiar with GitHub.
@@ -35,7 +35,11 @@ export default class GitHub { | |||
this.api.collaborator(user.login).then((status) => { | |||
if (status === 404 || status === 403) { | |||
// Unauthorized user | |||
<<<<<<< HEAD |
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.
Looks like you inadvertently committed a diff here.
Everything should be cleaned up now for a PR and the diff should've been addressed. |
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.
LGTM - thanks!
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.
Great changes, thanks for this! I have a couple nitpicks and a slightly bigger suggestion for refactoring the API code.
import styles from './AuthenticationPage.css'; | ||
|
||
export default class AuthenticationPage extends React.Component { | ||
static propTypes = { | ||
onLogin: React.PropTypes.func.isRequired, | ||
onLogin: React.PropTypes.func.isRequired |
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 is an unrelated change, and goes against our current eslint
config.
src/backends/github/API.js
Outdated
@@ -19,6 +19,10 @@ export default class API { | |||
return this.request("/user"); | |||
} | |||
|
|||
collaborator(user) { |
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 method should be renamed and modified as per my refactoring suggestions below.
@@ -60,6 +63,11 @@ export function loginUser(credentials) { | |||
dispatch(authenticate(user)); | |||
}) | |||
.catch((error) => { | |||
dispatch(notifSend({ |
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 is a long-awaited change.
.gitignore
Outdated
@@ -6,3 +6,6 @@ npm-debug.log | |||
.tern-project | |||
yarn-error.log | |||
.vscode/ | |||
manifest.yml | |||
.imdone/ | |||
config.local.yml |
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.
Don't know how I feel about these changes - on one hand, the .gitignore isn't super important; OTOH, I'd rather keep it small as it can cause confusion if it inadvertently ends up ignoring paths that we end up using, and if we add ignores for each contributor's individual tooling (rather than those that every contributor needs to ignore, such as build artifacts), I can see the .gitignore
becoming too large pretty quickly. Additionally, these changes show up in history and diffs despite being unrelated to the actual purpose of the PR. @erquhart thoughts?
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 personally think it's fine. manifest.yml
is a Bluemix artifact, and .imdone
is from Atom. A lengthy .gitignore
isn't a bad thing if it improves the developer experience for contributors.
@tortilaman what is config.local.yml
from?
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.
config.local.yml
is storing my backend config so I don't have to go searching for it to test with GitHub.
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.
@tortilaman let's leave that value out since it's not a standard file, but the other two can stay.
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.
@erquhart sounds like a solid policy to me, thanks for the clarification.
@@ -1,4 +1,7 @@ | |||
import { currentBackend } from '../backends/backend'; | |||
import { actions as notifActions } from 'redux-notifications'; | |||
|
|||
const { notifSend } = notifActions; |
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.
Today I learned you can't do full destructuring in an import
call - I was going to suggest import { actions: { notifSend } } from 'redux-notifications';
, but that syntax is only supported for assignment, not imports).
src/backends/github/API.js
Outdated
if (contentType && contentType.match(/json/)) { | ||
if (url.indexOf('/collaborators/') >= 0) { | ||
return responseStatus; | ||
} else if (contentType && contentType.match(/json/)) { |
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.
While I understand the motivation behind this code, this is bound to cause bugs in the future by special-casing a particular URL in what is otherwise a generic request function. This should really be done in collaborator
. One issue I see with the response
method as it stands is that JSON responses reject the promise when an error status is present, but text responses do not.
@erquhart if you agree that that should be fixed, I can include it in my GH API refactor branch I'm preparing to PR (the branch name is gh-api-refactors
, there's a little there already).
@tortilaman for now, you should be able to expect a JSON response from the GH API. Thus, any non-200 statuses will cause the promise to reject with the APIError
in the catch
block just below these lines. In addition, My suggestions for refactoring this are as follows:
- Remove this logic from
request
. - Rename the
collaborator
method toisCollaborator
, and have that return a promise that either rejects or resolves to a boolean, representing whether the user is a collaborator or not. - Catch the
404
,403
, and204
statuses inisCollaborator
. If the status is one of those three, resolve the promise to the appropriate boolean. Otherwise, rethrow the error. (remember, JSON responses reject the error if the status is not 200-99, so you don't need any additional - Remove the now-unnecessary status code checks from
authenticate
and change them to handle the boolean thatisCollaborator
is returning. Add acatch
block to handle unexpected errors, and move thethrow new Error('GitHub is not responding, please try again.');
to thecatch
block.
I'd be happy to elaborate on this further if necessary, and I can always refactor this myself if you'd rather move on, but either way I'd like this to be refactored before a merge.
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.
Great breakdown @Benaiah, I'm on board with this.
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.
@Benaiah Would isCollaborator
then need to follow promise syntax and utilize resolve()
etc.? I'm of the understanding that without doing that .then((response)...
will still work based on what I have now.
// Authorized user | ||
user.token = state.token; | ||
return user; | ||
} |
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.
The preceding status-checking logic should move to collaborator
/isCollaborator
(whatever that ends up being named) instead of being handled here - the only part of this that's relevant to authentication is whether the user is a collaborator or not, not the specific status code. This is part of the more detailed refactor I suggested above.
@Benaiah leaving this to you, merge once you're happy with the changes. |
@tortilaman merge conflict. |
# Conflicts: # src/backends/github/API.js
@Benaiah also, we should squash-merge. |
Apologies, I was definitely going to squash this on my last commit, but the interactive rebase got quite complicated with the merge and figured I would have to re-order commits and such, and I just figured it would be better left to you. |
No problem! Squash merging is a simple solution, no sweat. |
Just pulled master into this and some issues seem to have popped up. I'll update when I think it should be ready to merge again. |
@tortilaman sounds good, reach out if you need help. |
Change to check for generic push access instead of contributor status
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.
LGTM with one small change.
src/backends/github/API.js
Outdated
@@ -67,6 +80,9 @@ export default class API { | |||
if (contentType && contentType.match(/json/)) { | |||
return this.parseJsonResponse(response); | |||
} | |||
if (responseStatus !== 200) { |
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.
A generic request method like this should check the equivalent of response.ok
, which is "status in the range 200-299" (response.ok
on MDN, see also MDN's list of 2xx status codes). Changing it to this line would fix that:
if ((responseStatus >= 200) && (responseStatus <= 299)) {
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.
@Benaiah That's leftover from the collaborator check. I switched to a write permission check, so I'll just remove that actually.
Alright, everything resolved. |
@tortilaman thanks so much! |
Thanks for all the feedback! |
`isCollaborator` was created in #491 to block login if a user did not have write (push) permissions to a repo, by going through the list of a users repos until it found the right one. It did not institute pagination, however, so if a user had enough repos that the one in question was on another page, the CMS would assume that they did not have permission and block the login. This commit fixes the problem by calling the API for the specific repo instead of getting the whole list.
`isCollaborator` was created in #491 to block login if a user did not have write (push) permissions to a repo, by going through the list of a users repos until it found the right one. It did not institute pagination, however, so if a user had enough repos that the one in question was on another page, the CMS would assume that they did not have permission and block the login. This commit fixes the problem by calling the API for the specific repo instead of getting the whole list.
So far this is a basic fix for issue #323. Unauthorized users will not be able to get into the backend, and an alert window will popup informing them they need to speak with their site administrator to get access.
Once I figure out how to feed the error to AuthenticationPage.js I’ll make it look better.
Also, api_root wasn’t being fed to API.js, which was causing issues authenticating Github Enterprise users, since it was using the regular github api endpoint. I fed api_root in, which should enable Github Enterprise for everyone, and potentially other git providers?