Add support for changing GitHub OAuth permissions (#4)

This commit is contained in:
Tanuj Mittal 2020-05-29 19:16:50 -07:00 committed by GitHub
parent f308aeb929
commit a6c44e0e69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 10 deletions

View File

@ -1,9 +1,9 @@
import { DefaultButton, IButtonProps, PrimaryButton } from "office-ui-fabric-react";
import { DefaultButton, IButtonProps, Link, PrimaryButton } from "office-ui-fabric-react";
import * as React from "react";
import { IGitHubBranch, IGitHubRepo } from "../../../GitHub/GitHubClient";
import { AddRepoComponent, AddRepoComponentProps } from "./AddRepoComponent";
import { AuthorizeAccessComponent, AuthorizeAccessComponentProps } from "./AuthorizeAccessComponent";
import { ChildrenMargin, ButtonsFooterStyle, ContentFooterStyle } from "./GitHubStyleConstants";
import { ButtonsFooterStyle, ChildrenMargin, ContentFooterStyle } from "./GitHubStyleConstants";
import { ReposListComponent, ReposListComponentProps } from "./ReposListComponent";
export interface GitHubReposComponentProps {
@ -45,6 +45,9 @@ export class GitHubReposComponent extends React.Component<GitHubReposComponentPr
) : (
<>
<p>{GitHubReposComponent.ManageGitHubRepoDescription}</p>
<Link style={{ marginTop: ChildrenMargin }} onClick={this.props.resetConnection}>
{GitHubReposComponent.ManageGitHubRepoResetConnection}
</Link>
<ReposListComponent {...this.props.reposListProps} />
</>
);

View File

@ -60,20 +60,20 @@ describe("GitHubOAuthService", () => {
expect(gitHubOAuthService.getTokenObservable()()).toBeUndefined();
});
it("startOAuth resets OAuth state", () => {
it("startOAuth resets OAuth state", async () => {
let url: string;
const windowOpenCallback = jest.fn().mockImplementation((value: string) => {
url = value;
});
window.open = windowOpenCallback;
gitHubOAuthService.startOAuth("scope");
await gitHubOAuthService.startOAuth("scope");
expect(windowOpenCallback).toBeCalled();
const initialParams = new URLSearchParams(new URL(url).search);
expect(initialParams.get("state")).toBeDefined();
gitHubOAuthService.startOAuth("another scope");
await gitHubOAuthService.startOAuth("another scope");
expect(windowOpenCallback).toBeCalled();
const newParams = new URLSearchParams(new URL(url).search);
@ -106,7 +106,7 @@ describe("GitHubOAuthService", () => {
junoClient.getGitHubToken = getGitHubTokenCallback;
const initialToken = gitHubOAuthService.getTokenObservable()();
const state = gitHubOAuthService.startOAuth("scope");
const state = await gitHubOAuthService.startOAuth("scope");
const params: IGitHubConnectorParams = {
state,
@ -120,7 +120,7 @@ describe("GitHubOAuthService", () => {
});
it("finishOAuth updates token to error if state doesn't match", async () => {
gitHubOAuthService.startOAuth("scope");
await gitHubOAuthService.startOAuth("scope");
const params: IGitHubConnectorParams = {
state: "state",
@ -135,7 +135,7 @@ describe("GitHubOAuthService", () => {
const getGitHubTokenCallback = jest.fn().mockReturnValue({ status: HttpStatusCodes.NotFound });
junoClient.getGitHubToken = getGitHubTokenCallback;
const state = gitHubOAuthService.startOAuth("scope");
const state = await gitHubOAuthService.startOAuth("scope");
const params: IGitHubConnectorParams = {
state,

View File

@ -2,6 +2,7 @@ import ko from "knockout";
import { HttpStatusCodes } from "../Common/Constants";
import { Logger } from "../Common/Logger";
import { config } from "../Config";
import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import { JunoClient } from "../Juno/JunoClient";
import { isInvalidParentFrameOrigin } from "../Utils/MessageValidation";
@ -39,7 +40,19 @@ export class GitHubOAuthService {
this.token = ko.observable<IGitHubOAuthToken>();
}
public startOAuth(scope: string): string {
public async startOAuth(scope: string): Promise<string> {
// If attempting to change scope from "Public & private repos" to "Public only" we need to delete app authorization.
// Otherwise OAuth app still retains the "public & private repos" permissions.
if (
this.token()?.scope === AuthorizeAccessComponent.Scopes.PublicAndPrivate.key &&
scope === AuthorizeAccessComponent.Scopes.Public.key
) {
const logoutSuccessful = await this.logout();
if (!logoutSuccessful) {
return undefined;
}
}
const params = {
scope,
client_id: config.GITHUB_CLIENT_ID,
@ -76,7 +89,7 @@ export class GitHubOAuthService {
return this.token;
}
public async logout() {
public async logout(): Promise<boolean> {
try {
const response = await this.junoClient.deleteAppAuthorization(this.token()?.access_token);
if (response.status !== HttpStatusCodes.NoContent) {
@ -84,10 +97,12 @@ export class GitHubOAuthService {
}
this.resetToken();
return true;
} catch (error) {
const message = `Failed to delete app authorization: ${error}`;
Logger.logError(message, "GitHubOAuthService/logout");
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
return false;
}
}