diff --git a/package.json b/package.json index 98d31d54..26f62f49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "netlify-cms", - "version": "0.5.0-beta.1", + "version": "0.5.0-beta.7", "description": "Netlify CMS lets content editors work on structured content stored in git", "main": "dist/cms.js", "scripts": { @@ -68,6 +68,7 @@ "exports-loader": "^0.6.4", "extract-text-webpack-plugin": "^2.1.2", "file-loader": "^0.11.2", + "gotrue-js": "^0.9.3", "identity-obj-proxy": "^3.0.0", "imports-loader": "^0.7.1", "jest": "^20.0.4", @@ -101,7 +102,6 @@ "dateformat": "^1.0.12", "deep-equal": "^1.0.1", "fuzzy": "^0.1.1", - "gotrue-js": "^0.9.3", "history": "^2.1.2", "immutability-helper": "^2.0.0", "immutable": "^3.7.6", @@ -115,6 +115,7 @@ "mdast-util-definitions": "^1.2.2", "mdast-util-to-string": "^1.0.4", "moment": "^2.11.2", + "node-sass": "^3.10.0", "normalize.css": "^4.2.0", "preliminaries": "1.1.0", "preliminaries-parser-toml": "1.1.0", diff --git a/src/actions/auth.js b/src/actions/auth.js index 55d66c3f..10a586dd 100644 --- a/src/actions/auth.js +++ b/src/actions/auth.js @@ -77,7 +77,8 @@ export function logoutUser() { return (dispatch, getState) => { const state = getState(); const backend = currentBackend(state.config); - backend.logout(); - dispatch(logout()); + Promise.resolve(backend.logout()).then(() => { + dispatch(logout()); + }); }; } diff --git a/src/backends/backend.js b/src/backends/backend.js index 1f027628..782422bc 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -1,7 +1,7 @@ import { attempt, isError } from 'lodash'; import TestRepoBackend from "./test-repo/implementation"; import GitHubBackend from "./github/implementation"; -import NetlifyAuthBackend from "./netlify-auth/implementation"; +import GitGatewayBackend from "./git-gateway/implementation"; import { resolveFormat } from "../formats/formats"; import { selectListMethod, selectEntrySlug, selectEntryPath, selectAllowNewEntries, selectFolderEntryExtension } from "../reducers/collections"; import { createEntry } from "../valueObjects/Entry"; @@ -36,12 +36,12 @@ const slugFormatter = (template = "{{slug}}", entryData) => { const identifier = identifiers.find(ident => ident !== undefined); if (identifier === undefined) { - throw new Error("Collection must have a field name that is a valid entry identifier"); + throw new Error("Collection must have a field name that is a valid entry identifier"); } return identifier; }; - + return template.replace(/\{\{([^\}]+)\}\}/g, (_, field) => { switch (field) { case "year": @@ -88,11 +88,11 @@ class Backend { } logout() { - if (this.authStore) { - this.authStore.logout(); - } else { - throw new Error("User isn't authenticated."); - } + return Promise.resolve(this.implementation.logout()).then(() => { + if (this.authStore) { + this.authStore.logout(); + } + }); } getToken = () => this.implementation.getToken(); @@ -292,8 +292,8 @@ export function resolveBackend(config) { return new Backend(new TestRepoBackend(config), authStore); case "github": return new Backend(new GitHubBackend(config), authStore); - case "netlify-auth": - return new Backend(new NetlifyAuthBackend(config), authStore); + case "git-gateway": + return new Backend(new GitGatewayBackend(config), authStore); default: throw new Error(`Backend not found: ${ name }`); } diff --git a/src/backends/netlify-auth/API.js b/src/backends/git-gateway/API.js similarity index 100% rename from src/backends/netlify-auth/API.js rename to src/backends/git-gateway/API.js diff --git a/src/backends/netlify-auth/AuthenticationPage.css b/src/backends/git-gateway/AuthenticationPage.css similarity index 100% rename from src/backends/netlify-auth/AuthenticationPage.css rename to src/backends/git-gateway/AuthenticationPage.css diff --git a/src/backends/netlify-auth/AuthenticationPage.js b/src/backends/git-gateway/AuthenticationPage.js similarity index 67% rename from src/backends/netlify-auth/AuthenticationPage.js rename to src/backends/git-gateway/AuthenticationPage.js index d23aee69..66807e56 100644 --- a/src/backends/netlify-auth/AuthenticationPage.js +++ b/src/backends/git-gateway/AuthenticationPage.js @@ -1,28 +1,54 @@ import React from "react"; import Input from "react-toolbox/lib/input"; import Button from "react-toolbox/lib/button"; +import { Notifs } from 'redux-notifications'; +import { Toast } from '../../components/UI/index'; import { Card, Icon } from "../../components/UI"; import logo from "./netlify_logo.svg"; import styles from "./AuthenticationPage.css"; +let component = null; + +if (window.netlifyIdentity) { + window.netlifyIdentity.on('login', (user) => { + component && component.handleIdentityLogin(user); + }); + window.netlifyIdentity.on('logout', () => { + component && component.handleIdentityLogout(); + }); +} + export default class AuthenticationPage extends React.Component { constructor(props) { super(props); - this.identity = window.netlifyIdentity; - this.state = {user: this.identity && this.identity.gotrue && this.identity.gotrue.currentUser()}; + component = this; } componentDidMount() { - if (this.identity && !this.state.user) { - this.identity.on('login', (user) => { - this.props.onLogin(user); - this.identity.close(); - }); - this.identity.on('signup', (user) => { - this.props.onLogin(user); - this.identity.close(); - }); - this.identity.open(); + if (!this.loggedIn && window.netlifyIdentity && window.netlifyIdentity.currentUser()) { + this.props.onLogin(window.netlifyIdentity.currentUser()); + window.netlifyIdentity.close(); + } + } + + componentWillUnmount() { + component = null; + } + + handleIdentityLogin = (user) => { + this.props.onLogin(user); + window.netlifyIdentity.close(); + } + + handleIdentityLogout = () => { + window.netlifyIdentity.open(); + } + + handleIdentity = () => { + if (window.netlifyIdentity.currentUser()) { + this.props.onLogin(user); + } else { + window.netlifyIdentity.open(); } } @@ -66,6 +92,15 @@ export default class AuthenticationPage extends React.Component { const { errors } = this.state; const { error } = this.props; + if (window.netlifyIdentity) { + return
+ + +
+ } + return (
diff --git a/src/backends/netlify-auth/implementation.js b/src/backends/git-gateway/implementation.js similarity index 65% rename from src/backends/netlify-auth/implementation.js rename to src/backends/git-gateway/implementation.js index 66776c5c..2b1f58ee 100644 --- a/src/backends/netlify-auth/implementation.js +++ b/src/backends/git-gateway/implementation.js @@ -11,38 +11,40 @@ const localHosts = { '127.0.0.1': true, '0.0.0.0': true } +const defaults = { + identity: '/.netlify/identity', + gateway: '/.netlify/git/github' +} function getEndpoint(endpoint, netlifySiteURL) { - if (localHosts[document.location.host] && netlifySiteURL && endpoint.match(/^\/\.netlify\//)) { - const parts = [netlifySiteURL]; - if (!netlifySiteURL.match(/\/$/)) { parts.push("/"); } - parts.push(endpoint); + if (localHosts[document.location.host.split(":").shift()] && netlifySiteURL && endpoint.match(/^\/\.netlify\//)) { + const parts = []; + if (netlifySiteURL) { + parts.push(netlifySiteURL); + if (!netlifySiteURL.match(/\/$/)) { parts.push("/"); } + } + parts.push(endpoint.replace(/^\//, '')); return parts.join(""); } return endpoint; } -export default class NetlifyAuth extends GitHubBackend { +export default class GitGateway extends GitHubBackend { constructor(config) { super(config, true); - if (config.getIn(["backend", "auth_url"]) == null) { throw new Error("The NetlifyAuth backend needs an \"auth_url\" in the backend configuration."); } - - if (config.getIn(["backend", "github_proxy_url"]) == null) { - throw new Error("The NetlifyAuth backend needs an \"github_proxy_url\" in the backend configuration."); - } this.accept_roles = (config.getIn(["backend", "accept_roles"]) || List()).toArray(); const netlifySiteURL = localStorage.getItem("netlifySiteURL"); - const APIUrl = getEndpoint(config.getIn(["backend", "auth_url"]), netlifySiteURL); - this.github_proxy_url = getEndpoint(config.getIn(["backend", "github_proxy_url"]), netlifySiteURL); - this.authClient = new GoTrue({APIUrl}); + const APIUrl = getEndpoint(config.getIn(["backend", "identity_url"], defaults.identity), netlifySiteURL); + this.github_proxy_url = getEndpoint(config.getIn(["backend", "gateway_url"], defaults.gateway), netlifySiteURL); + this.authClient = window.netlifyIdentity ? window.netlifyIdentity.gotrue : new GoTrue({APIUrl}); AuthenticationPage.authClient = this.authClient; } setUser() { - const user = this.authClient.currentUser(); + const user = this.authClient && this.authClient.currentUser(); if (!user) return Promise.reject(); return this.authenticate(user); } @@ -58,7 +60,7 @@ export default class NetlifyAuth extends GitHubBackend { const userRoles = get(jwtDecode(token), 'app_metadata.roles', []); if (validRole) { const userData = { - name: user.user_metadata.name, + name: user.user_metadata.name || user.email.split('@').shift(), email: user.email, metadata: user.user_metadata, }; @@ -74,6 +76,14 @@ export default class NetlifyAuth extends GitHubBackend { }); } + logout() { + if (window.netlifyIdentity) { + return window.netlifyIdentity.logout(); + } + const user = this.authClient.currentUser(); + return user && user.logout(); + } + getToken() { return this.tokenPromise(); } diff --git a/src/backends/netlify-auth/netlify_logo.svg b/src/backends/git-gateway/netlify_logo.svg similarity index 100% rename from src/backends/netlify-auth/netlify_logo.svg rename to src/backends/git-gateway/netlify_logo.svg diff --git a/src/backends/github/implementation.js b/src/backends/github/implementation.js index 8f978fa0..f071d021 100644 --- a/src/backends/github/implementation.js +++ b/src/backends/github/implementation.js @@ -42,6 +42,11 @@ export default class GitHub { ); } + logout() { + this.token = null; + return; + } + getToken() { return Promise.resolve(this.token); } diff --git a/src/backends/test-repo/AuthenticationPage.js b/src/backends/test-repo/AuthenticationPage.js index 92ac194d..6ebe77c0 100644 --- a/src/backends/test-repo/AuthenticationPage.js +++ b/src/backends/test-repo/AuthenticationPage.js @@ -2,8 +2,8 @@ import React from 'react'; import Input from "react-toolbox/lib/input"; import Button from "react-toolbox/lib/button"; import { Card, Icon } from "../../components/UI"; -import logo from "../netlify-auth/netlify_logo.svg"; -import styles from "../netlify-auth/AuthenticationPage.css"; +import logo from "../git-gateway/netlify_logo.svg"; +import styles from "../git-gateway/AuthenticationPage.css"; export default class AuthenticationPage extends React.Component { static propTypes = { diff --git a/src/backends/test-repo/implementation.js b/src/backends/test-repo/implementation.js index 18180aa3..c7cc22c7 100644 --- a/src/backends/test-repo/implementation.js +++ b/src/backends/test-repo/implementation.js @@ -36,6 +36,10 @@ export default class TestRepo { return Promise.resolve({ email: state.email, name: nameFromEmail(state.email) }); } + logout() { + return null; + } + getToken() { return Promise.resolve(''); }