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

Address issue #323 and enable GHE Auth #491

Merged
merged 10 commits into from
Aug 2, 2017
Merged

Conversation

tortilaman
Copy link
Contributor

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?

@erquhart
Copy link
Contributor

@tortilaman fyi I rebased your branch so that only the new commits are included in this PR.

Copy link
Contributor

@erquhart erquhart left a 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": {
Copy link
Contributor

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.

@tortilaman
Copy link
Contributor Author

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.

@tortilaman
Copy link
Contributor Author

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.

@erquhart
Copy link
Contributor

Fixed history again - if you do a hard reset to origin from your local repo, that should prevent further extraneous commits.

Copy link
Contributor

@erquhart erquhart left a 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.');
Copy link
Contributor

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."

Copy link
Contributor Author

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.");
Copy link
Contributor

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."

Copy link
Contributor Author

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
Copy link
Contributor

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.

@tortilaman
Copy link
Contributor Author

Everything should be cleaned up now for a PR and the diff should've been addressed.

Copy link
Contributor

@erquhart erquhart left a comment

Choose a reason for hiding this comment

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

LGTM - thanks!

Copy link
Contributor

@Benaiah Benaiah left a 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
Copy link
Contributor

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.

@@ -19,6 +19,10 @@ export default class API {
return this.request("/user");
}

collaborator(user) {
Copy link
Contributor

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({
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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;
Copy link
Contributor

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).

if (contentType && contentType.match(/json/)) {
if (url.indexOf('/collaborators/') >= 0) {
return responseStatus;
} else if (contentType && contentType.match(/json/)) {
Copy link
Contributor

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 to isCollaborator, 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, and 204 statuses in isCollaborator. 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 that isCollaborator is returning. Add a catch block to handle unexpected errors, and move the throw new Error('GitHub is not responding, please try again.'); to the catch 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.

Copy link
Contributor

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.

Copy link
Contributor Author

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;
}
Copy link
Contributor

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.

@erquhart
Copy link
Contributor

@Benaiah leaving this to you, merge once you're happy with the changes.

@erquhart
Copy link
Contributor

@tortilaman merge conflict.

Nick Doering added 2 commits July 24, 2017 09:27
# Conflicts:
#	src/backends/github/API.js
@erquhart
Copy link
Contributor

@Benaiah also, we should squash-merge.

@tortilaman
Copy link
Contributor Author

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.

@erquhart
Copy link
Contributor

No problem! Squash merging is a simple solution, no sweat.

@tortilaman
Copy link
Contributor Author

tortilaman commented Jul 31, 2017

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.

@erquhart
Copy link
Contributor

erquhart commented Aug 1, 2017

@tortilaman sounds good, reach out if you need help.

Nick Doering added 2 commits August 1, 2017 11:15
@tortilaman
Copy link
Contributor Author

Alright @erquhart @Benaiah I think this is ready for a squash merge.

Copy link
Contributor

@Benaiah Benaiah left a 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.

@@ -67,6 +80,9 @@ export default class API {
if (contentType && contentType.match(/json/)) {
return this.parseJsonResponse(response);
}
if (responseStatus !== 200) {
Copy link
Contributor

@Benaiah Benaiah Aug 1, 2017

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)) {

Copy link
Contributor Author

@tortilaman tortilaman Aug 1, 2017

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.

@tortilaman
Copy link
Contributor Author

Alright, everything resolved.

@Benaiah Benaiah merged commit 6805a69 into decaporg:master Aug 2, 2017
@Benaiah
Copy link
Contributor

Benaiah commented Aug 2, 2017

@tortilaman thanks so much!

@tortilaman
Copy link
Contributor Author

Thanks for all the feedback!

tech4him1 added a commit that referenced this pull request Aug 19, 2017
`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.
tech4him1 added a commit that referenced this pull request Aug 20, 2017
`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.
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.

None yet

3 participants