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

Sign In With GitHub #356

Merged
merged 1 commit into from
Apr 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions web/app/AppComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
// is made available under an open source license such as the Apache 2.0 License.

import {AppStore} from "./AppStore";
import {Component} from "angular2/core";
import {Component, OnInit} from "angular2/core";
import {ExplorePageComponent} from "./explore-page/ExplorePageComponent";
import {HeaderComponent} from "./header/HeaderComponent";
import {LinkedAccountsPageComponent} from "./linked-accounts-page/LinkedAccountsPageComponent";
import {NotificationsComponent} from "./notifications/NotificationsComponent";
import {OrganizationCreatePageComponent} from "./organization-create-page/OrganizationCreatePageComponent";
import {OrganizationsPageComponent} from "./organizations-page/OrganizationsPageComponent";
Expand All @@ -21,7 +20,7 @@ import {RouteConfig, Router, RouterOutlet} from "angular2/router";
import {SCMReposPageComponent} from "./scm-repos-page/SCMReposPageComponent";
import {SideNavComponent} from "./side-nav/SideNavComponent";
import {SignInPageComponent} from "./sign-in-page/SignInPageComponent";
import {removeNotification, routeChange, signOutViaUserNavMenu,
import {authenticateWithGitHub, removeNotification, routeChange, signOut,
toggleUserNavMenu} from "./actions/index";

@Component({
Expand All @@ -36,8 +35,8 @@ import {removeNotification, routeChange, signOutViaUserNavMenu,
[isUserNavOpen]="user.isUserNavOpen"
[isSignedIn]="user.isSignedIn"
[username]="user.username"
[avatarUrl]="user.avatarUrl"
[signOutViaUserNavMenu]="signOutViaUserNavMenu"
[avatarUrl]="user.gitHub.get('avatar_url')"
[signOut]="signOut"
[toggleUserNavMenu]="toggleUserNavMenu"></hab-header>
</div>
<div class="hab-container">
Expand All @@ -63,11 +62,6 @@ import {removeNotification, routeChange, signOutViaUserNavMenu,
name: "Explore",
component: ExplorePageComponent
},
{
path: "/linked-accounts",
name: "LinkedAccounts",
component: LinkedAccountsPageComponent,
},
{
path: "/orgs",
name: "Organizations",
Expand Down Expand Up @@ -135,9 +129,9 @@ import {removeNotification, routeChange, signOutViaUserNavMenu,
},
])

export class AppComponent {
export class AppComponent implements OnInit {
removeNotification: Function;
signOutViaUserNavMenu: Function;
signOut: Function;
toggleUserNavMenu: Function;

constructor(private router: Router, private store: AppStore) {
Expand All @@ -158,8 +152,8 @@ export class AppComponent {
return false;
}.bind(this);

this.signOutViaUserNavMenu = function() {
this.store.dispatch(signOutViaUserNavMenu());
this.signOut = function() {
this.store.dispatch(signOut());
return false;
}.bind(this);

Expand All @@ -170,9 +164,15 @@ export class AppComponent {

}

get origin() { return {}; }
get origin() { return this.state.origins.current; }

get state() { return this.store.getState(); }

get user() { return this.state.users.current; }

ngOnInit() {
// When the page loads attempt to authenticate with GitHub. If there
// is no token stored in session storage, this won't do anything.
this.store.dispatch(authenticateWithGitHub());
}
}
101 changes: 75 additions & 26 deletions web/app/actions/gitHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,52 @@
// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software
// is made available under an open source license such as the Apache 2.0 License.

import "whatwg-fetch";
import config from "../config";
import * as fakeApi from "../fakeApi";
import {requestRoute} from "./router";
import {attemptSignIn, addNotification, goHome, requestRoute, setCurrentOrigin,
signOut} from "./index";
import {DANGER} from "./notifications";

const uuid = require("node-uuid").v4;
const gitHubTokenAuthUrl = config["github_token_auth_url"];

export const LINK_GITHUB_ACCOUNT = "LINK_GITHUB_ACCOUNT";
export const LINK_GITHUB_ACCOUNT_SUCCESS = "LINK_GITHUB_ACCOUNT_SUCCESS";
export const POPULATE_GITHUB_REPOS = "POPULATE_GITHUB_REPOS";
export const POPULATE_GITHUB_USER_DATA = "POPULATE_GITHUB_USER_DATA";
export const SET_GITHUB_AUTH_STATE = "SET_GITHUB_AUTH_STATE";
export const SET_SELECTED_GITHUB_ORG = "SET_SELECTED_GITHUB_ORG";
export const UNLINK_GITHUB_ACCOUNT = "UNLINK_GITHUB_ACCOUNT";
export const UNLINK_GITHUB_ACCOUNT_SUCCESS = "UNLINK_GITHUB_ACCOUNT";

export function fetchGitHubRepos() {
return dispatch => {
fakeApi.get("github/user/repos.json").then(response => {
dispatch(populateGitHubRepos(response));
});
};
}
export function authenticateWithGitHub(token = undefined) {
token = token || sessionStorage.getItem("gitHubAuthToken");

export function linkGitHubAccount(username) {
return dispatch => {
window["githubWindow"] = window.open("/fixtures/authorizeGitHub.html");
dispatch(linkGitHubAccountSuccess(username));
if (token) {
sessionStorage.setItem("gitHubAuthToken", token);

fetch(`https://api.github.com/user?access_token=${token}`).then(response => {
if (response["status"] === 401) {
// When we get an unauthorized response, out token is no
// longer valid, so sign out.
dispatch(signOut());
return false;
} else {
return response.json();
}
}).then(data => {
dispatch(setCurrentOrigin({ name: data["login"]}));
dispatch(populateGitHubUserData(data));
dispatch(attemptSignIn(data["login"]));
dispatch(goHome());
});
}
};
}

function linkGitHubAccountSuccess(username) {
return {
type: LINK_GITHUB_ACCOUNT_SUCCESS,
payload: username,
export function fetchGitHubRepos() {
return dispatch => {
fakeApi.get("github/user/repos.json").then(response => {
dispatch(populateGitHubRepos(response));
});
};
}

Expand All @@ -51,21 +68,53 @@ function populateGitHubRepos(data) {
};
}

export function setSelectedGitHubOrg(org) {
function populateGitHubUserData(payload) {
return {
type: SET_SELECTED_GITHUB_ORG,
payload: org,
type: POPULATE_GITHUB_USER_DATA,
payload,
};
}

export function unlinkGitHubAccount() {
export function requestGitHubAuthToken(params = {}, stateKey = "") {
return dispatch => {
dispatch(unlinkGitHubAccountSuccess());
if (params["code"] && params["state"] === stateKey) {
fetch(`${gitHubTokenAuthUrl}/${params["code"]}`).then(response => {
return response.json();
}).catch(error => {
console.error(error);
dispatch(addNotification({
title: "Authentication Failed",
body: "Unable to retrieve GitHub token",
type: DANGER,
}));
}).then(data => {
if (data["token"]) {
dispatch(authenticateWithGitHub(data["token"]));
} else {
dispatch(addNotification({
title: "Authentication Failed",
body: data["error"],
type: DANGER,
}));
}
});
}
};
}

function unlinkGitHubAccountSuccess() {
export function setGitHubAuthState() {
let payload = sessionStorage.getItem("gitHubAuthState") || uuid();
sessionStorage.setItem("gitHubAuthState", payload);

return {
type: UNLINK_GITHUB_ACCOUNT,
type: SET_GITHUB_AUTH_STATE,
payload
};
}

export function setSelectedGitHubOrg(org) {
return {
type: SET_SELECTED_GITHUB_ORG,
payload: org,
};
}
19 changes: 12 additions & 7 deletions web/app/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@
import * as gitHubActions from "./gitHub";
import * as notificationActions from "./notifications";
import * as orgActions from "./orgs";
import * as originActions from "./origins";
import * as packageActions from "./packages";
import * as projectActions from "./projects";
import * as routerActions from "./router";
import * as usersActions from "./users";

// Action types
export const LINK_GITHUB_ACCOUNT = gitHubActions.LINK_GITHUB_ACCOUNT;
export const LINK_GITHUB_ACCOUNT_SUCCESS = gitHubActions.LINK_GITHUB_ACCOUNT_SUCCESS;
export const POPULATE_GITHUB_REPOS = gitHubActions.POPULATE_GITHUB_REPOS;
export const POPULATE_GITHUB_USER_DATA = gitHubActions.POPULATE_GITHUB_USER_DATA;
export const SET_GITHUB_AUTH_STATE = gitHubActions.SET_GITHUB_AUTH_STATE;
export const SET_SELECTED_GITHUB_ORG = gitHubActions.SET_SELECTED_GITHUB_ORG;
export const UNLINK_GITHUB_ACCOUNT = gitHubActions.UNLINK_GITHUB_ACCOUNT;
export const UNLINK_GITHUB_ACCOUNT_SUCCESS = gitHubActions.UNLINK_GITHUB_ACCOUNT_SUCCESS;

export const ADD_NOTIFICATION = notificationActions.ADD_NOTIFICATION;
export const REMOVE_NOTIFICATION = notificationActions.REMOVE_NOTIFICATION;
Expand All @@ -30,6 +29,8 @@ export const PERFORM_ORG_MEMBER_SEARCH = orgActions.PERFORM_ORG_MEMBER_SEARCH;
export const POPULATE_ORG = orgActions.POPULATE_ORG;
export const TOGGLE_MEMBER_ACTION_MENU = orgActions.TOGGLE_MEMBER_ACTION_MENU;

export const SET_CURRENT_ORIGIN = originActions.SET_CURRENT_ORIGIN;

export const CLEAR_PACKAGES = packageActions.CLEAR_PACKAGES;
export const POPULATE_EXPLORE = packageActions.POPULATE_EXPLORE;
export const SET_CURRENT_PACKAGE = packageActions.SET_CURRENT_PACKAGE;
Expand All @@ -47,17 +48,19 @@ export const ROUTE_CHANGE = routerActions.ROUTE_CHANGE;
export const ROUTE_REQUESTED = routerActions.ROUTE_REQUESTED;

export const SIGN_IN_ATTEMPT = usersActions.SIGN_IN_ATTEMPT;

export const TOGGLE_USER_NAV_MENU = usersActions.TOGGLE_USER_NAV_MENU;

// Used by redux-reset to reset the app state
export const RESET = "RESET";

// Actions
export const authenticateWithGitHub = gitHubActions.authenticateWithGitHub;
export const fetchGitHubRepos = gitHubActions.fetchGitHubRepos;
export const linkGitHubAccount = gitHubActions.linkGitHubAccount;
export const onGitHubRepoSelect = gitHubActions.onGitHubRepoSelect;
export const requestGitHubAuthToken = gitHubActions.requestGitHubAuthToken;
export const setGitHubAuthState = gitHubActions.setGitHubAuthState;
export const setSelectedGitHubOrg = gitHubActions.setSelectedGitHubOrg;
export const unlinkGitHubAccount = gitHubActions.unlinkGitHubAccount;

export const addNotification = notificationActions.addNotification;
export const removeNotification = notificationActions.removeNotification;
Expand All @@ -69,6 +72,8 @@ export const inviteMemberToOrg = orgActions.inviteMemberToOrg;
export const performOrgMemberSearch = orgActions.performOrgMemberSearch;
export const toggleMemberActionMenu = orgActions.toggleMemberActionMenu;

export const setCurrentOrigin = originActions.setCurrentOrigin;

export const fetchExplore = packageActions.fetchExplore;
export const fetchPackage = packageActions.fetchPackage;
export const filterPackagesBy = packageActions.filterPackagesBy;
Expand All @@ -89,7 +94,7 @@ export const requestRoute = routerActions.requestRoute;

export const attemptSignIn = usersActions.attemptSignIn;
export const toggleUserNavMenu = usersActions.toggleUserNavMenu;
export const signOutViaUserNavMenu = usersActions.signOutViaUserNavMenu;
export const signOut = usersActions.signOut;

export function resetAppState() {
return {
Expand Down
14 changes: 14 additions & 0 deletions web/app/actions/origins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright:: Copyright (c) 2016 Chef Software, Inc.
//
// The terms of the Evaluation Agreement (Bldr) between Chef Software Inc. and the party accessing
// this file ("Licensee") apply to Licensee's use of the Software until such time that the Software
// is made available under an open source license such as the Apache 2.0 License.

export const SET_CURRENT_ORIGIN = "SET_CURRENT_ORIGIN";

export function setCurrentOrigin(payload) {
return {
type: SET_CURRENT_ORIGIN,
payload,
};
}
4 changes: 3 additions & 1 deletion web/app/actions/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export function toggleUserNavMenu() {
};
}

export function signOutViaUserNavMenu() {
export function signOut() {
sessionStorage.removeItem("gitHubAuthToken");

return dispatch => {
dispatch(resetAppState());
dispatch(requestRoute(["SignIn"]));
Expand Down
2 changes: 1 addition & 1 deletion web/app/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@import "bourbon";
@import "base/base";
@import "neat";
@import "octicons.scss";

@import "drop-down";
@import "item-list";
Expand All @@ -18,7 +19,6 @@
@import "github-repo-picker/github-repo-picker";
@import "header/header";
@import "header/user-nav/user-nav";
@import "linked-accounts-page/linked-accounts-page";
@import "notifications/notifications";
@import "organization-create-page/organization-create-page";
@import "organization-members/organization-members";
Expand Down
2 changes: 1 addition & 1 deletion web/app/depotApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import "whatwg-fetch";
import config from "./config";
import {packageString} from "./util";

const urlPrefix = config["depotUrl"] || "";
const urlPrefix = config["depot_url"] || "";

// Get the JSON from a url from the fixtures directory.
export function get(ident) {
Expand Down
28 changes: 19 additions & 9 deletions web/app/header/HeaderComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,35 @@
// is made available under an open source license such as the Apache 2.0 License.

import {Component} from "angular2/core";
import {RouterLink} from "angular2/router";
import config from "../config";
import {UserNavComponent} from "./user-nav/UserNavComponent";

@Component({
directives: [UserNavComponent],
directives: [RouterLink, UserNavComponent],
inputs: ["appName", "isUserNavOpen", "isSignedIn", "username", "avatarUrl",
"signOutViaUserNavMenu", "toggleUserNavMenu"],
"signOut", "toggleUserNavMenu"],
selector: "hab-header",
template: `
<header class="hab-header">
<h1 class="logo">{{appName}}</h1>
<nav>
<user-nav [isOpen]="isUserNavOpen"
[isSignedIn]="isSignedIn"
[username]="username"
[avatarUrl]="avatarUrl"
[signOutViaUserNavMenu]="signOutViaUserNavMenu"
[toggleUserNavMenu]="toggleUserNavMenu"></user-nav>
<ul>
<li><a [routerLink]="['Packages']">Packages</a></li>
<li><a href="{{config['docs_url']}}">Docs</a></li>
<li><a href="{{config['tutorials_url']}}">Tutorials</a></li>
<li><a href="{{config['community_url']}}">Community</a></li>
<user-nav [isOpen]="isUserNavOpen"
[isSignedIn]="isSignedIn"
[username]="username"
[avatarUrl]="avatarUrl"
[signOut]="signOut"
[toggleUserNavMenu]="toggleUserNavMenu"></user-nav>
</ul>
</nav>
</header>`,
})

export class HeaderComponent { }
export class HeaderComponent {
get config() { return config; }
}
Loading