mirror of
https://github.com/wassname/talk.git
synced 2026-09-09 11:38:08 +08:00
[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:
@@ -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;
|
||||
+83
@@ -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: <username></username>
|
||||
</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;
|
||||
Reference in New Issue
Block a user