[next] Admin Authentication (#2155)

* feat: Support sign in via E-Mail, and social logins + permission check

* feat: no permission info

* test: unit test

* test: fix remaining tests

* test: add integration tests

* feat: add admin account completion

* test: auth completion feature tests

* fix: translation

* fix: comment marker design

* fix: linting issues

* chore: address pr review comments

* chore: add comment
This commit is contained in:
Kiwi
2019-02-04 21:26:41 +00:00
committed by Wyatt Johnson
parent 939152ee81
commit 7e8ef2189d
143 changed files with 5991 additions and 2048 deletions
@@ -0,0 +1,9 @@
.lockIcon {
color: var(--palette-grey-dark);
}
.noPermission {
color: var(--palette-grey-dark);
}
.contactAdmin {
color: var(--palette-grey-main);
}
@@ -0,0 +1,18 @@
import { noop } from "lodash";
import React from "react";
import { createRenderer } from "react-test-renderer/shallow";
import { PropTypesOf } from "talk-framework/types";
import Restricted from "./Restricted";
it("renders correctly", () => {
const props: PropTypesOf<typeof Restricted> = {
username: "User",
onSignInAs: noop,
};
const renderer = createRenderer();
renderer.render(<Restricted {...props} />);
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
@@ -0,0 +1,80 @@
import { Localized } from "fluent-react/compat";
import React, { StatelessComponent } from "react";
import AuthBox from "talk-admin/components/AuthBox";
import {
Button,
Flex,
HorizontalGutter,
Icon,
Typography,
} from "talk-ui/components";
import styles from "./Restricted.css";
interface Props {
username: string;
onSignInAs: React.MouseEventHandler;
}
const SignIn: StatelessComponent<Props> = ({ username, onSignInAs }) => {
const Username = () => (
<Typography variant="heading1" align="center">
{username}
</Typography>
);
return (
<AuthBox
title={
<Localized id="restricted-currentlySignedInTo">
<span>Currently signed in to</span>
</Localized>
}
>
<HorizontalGutter size="double">
<div>
<Flex justifyContent="center">
<Icon size="lg" className={styles.lockIcon}>
lock
</Icon>
</Flex>
<Localized id="restricted-noPermissionInfo">
<Typography
variant="heading3"
align="center"
className={styles.noPermission}
>
You do not have permission to access this page.
</Typography>
</Localized>
</div>
<div>
<Localized id="restricted-signedInAs" username={<Username />}>
<Typography variant="bodyCopy" align="center" container="div">
{"You are signed in as: <username></username>"}
</Typography>
</Localized>
</div>
<Flex justifyContent="center">
<Localized id="restricted-signInWithADifferentAccount">
<Button variant="filled" color="primary" onClick={onSignInAs}>
Sign in with a different account
</Button>
</Localized>
</Flex>
<Localized id="restricted-contactAdmin">
<Typography
variant="bodyCopy"
align="center"
className={styles.contactAdmin}
>
If you think this is an error, please contact your administrator for
assistance.
</Typography>
</Localized>
</HorizontalGutter>
</AuthBox>
);
};
export default SignIn;
@@ -0,0 +1,83 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<AuthBox
title={
<Localized
id="restricted-currentlySignedInTo"
>
<span>
Currently signed in to
</span>
</Localized>
}
>
<withPropsOnChange(HorizontalGutter)
size="double"
>
<div>
<withPropsOnChange(Flex)
justifyContent="center"
>
<withPropsOnChange(Icon)
className="Restricted-lockIcon"
size="lg"
>
lock
</withPropsOnChange(Icon)>
</withPropsOnChange(Flex)>
<Localized
id="restricted-noPermissionInfo"
>
<withPropsOnChange(Typography)
align="center"
className="Restricted-noPermission"
variant="heading3"
>
You do not have permission to access this page.
</withPropsOnChange(Typography)>
</Localized>
</div>
<div>
<Localized
id="restricted-signedInAs"
username={<Username />}
>
<withPropsOnChange(Typography)
align="center"
container="div"
variant="bodyCopy"
>
You are signed in as: &lt;username&gt;&lt;/username&gt;
</withPropsOnChange(Typography)>
</Localized>
</div>
<withPropsOnChange(Flex)
justifyContent="center"
>
<Localized
id="restricted-signInWithADifferentAccount"
>
<withPropsOnChange(Button)
color="primary"
onClick={[Function]}
variant="filled"
>
Sign in with a different account
</withPropsOnChange(Button)>
</Localized>
</withPropsOnChange(Flex)>
<Localized
id="restricted-contactAdmin"
>
<withPropsOnChange(Typography)
align="center"
className="Restricted-contactAdmin"
variant="bodyCopy"
>
If you think this is an error, please contact your administrator for assistance.
</withPropsOnChange(Typography)>
</Localized>
</withPropsOnChange(HorizontalGutter)>
</AuthBox>
`;
@@ -0,0 +1,56 @@
import { RouteProps } from "found";
import React, { Component } from "react";
import { RestrictedContainer_me as MeData } from "talk-admin/__generated__/RestrictedContainer_me.graphql";
import {
SetRedirectPathMutation,
withSetRedirectPathMutation,
} from "talk-admin/mutations";
import { graphql, withFragmentContainer } from "talk-framework/lib/relay";
import { SignOutMutation, withSignOutMutation } from "talk-framework/mutations";
import { timeout } from "talk-common/utils";
import Restricted from "../components/Restricted";
interface Props {
me: MeData;
error?: Error | null;
signOut: SignOutMutation;
setRedirectPath: SetRedirectPathMutation;
}
class RestrictedContainer extends Component<Props> {
public static routeConfig: RouteProps;
private handleSignInAs = async () => {
await this.props.signOut();
// Wait for new context to propagate.
await timeout();
this.props.setRedirectPath({
path: location.pathname + location.search + location.hash,
});
};
public render() {
if (!this.props.me) {
return null;
}
return (
<Restricted
username={this.props.me.username!}
onSignInAs={this.handleSignInAs}
/>
);
}
}
const enhanced = withFragmentContainer<Props>({
me: graphql`
fragment RestrictedContainer_me on User {
username
}
`,
})(withSetRedirectPathMutation(withSignOutMutation(RestrictedContainer)));
export default enhanced;