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

Avoid same user in multi devices #47

Merged
merged 11 commits into from
Oct 29, 2023
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import P2PCallMain from "./features/p2p-call/P2PCallMain";
import CallCreationMain from "./features/call-selection/CallCreationMain";
import CallJoinMain from "./features/call-selection/CallJoinMain";
import PendingUserMain from "./features/call-selection/PendingUserMain";
import useValidateSession from "./hooks/useValidateSession";
import BlockedSession from "./features/auth/BlockedSession";

export default function App() {
useValidateSession();
const dispatch = useAppDispatch();

useEffect(() => {
Expand All @@ -29,6 +32,7 @@ export default function App() {
<Route path="/join" component={CallJoinMain} />
<Route path="/left" component={() => <p>Left.</p>} />
<Route path="/p2p-call/:callUid" component={P2PCallMain} />
<Route path="/blocked-session" component={BlockedSession} />
<Route>
<Redirect to="/" />
</Route>
Expand Down
21 changes: 21 additions & 0 deletions src/components/templates/HomeTemplate.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,25 @@ describe("HomeTemplate", () => {
)
).toBe(null);
});

it("does not show logout and settings buttons when user session is blocked", () => {
fullRender(
<HomeTemplate>
<p>Hello World!</p>
</HomeTemplate>,
{
preloadedState: {
user: {
...userInitialState,
uid: "123",
status: "authenticated",
isSessionBlocked: true,
},
},
}
);

expect(screen.queryByLabelText("Logout")).toBe(null);
expect(screen.queryByLabelText("Settings")).toBe(null);
});
});
25 changes: 16 additions & 9 deletions src/components/templates/HomeTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import mainCharWhiteOutlineImg from "../../assets/main-char-white-outline.png";
import mainCharPinkOutlineImg from "../../assets/main-char-pink-outline.png";
import { useAppDispatch, useAppSelector } from "../../state";
import useAgentHelper from "../../hooks/useAgentHelper";
import { logout, selectIsUserAuthenticated } from "../../state/user";
import {
logout,
selectIsSessionBlocked,
selectIsUserAuthenticated,
} from "../../state/user";
import SettingsModal from "../settings/SettingsModal";
import ErrorAlert from "../basic/ErrorAlert";
import WarningAlert from "../basic/WarningAlert";
Expand All @@ -32,6 +36,7 @@ export default function HomeTemplate({
const handleLogoutClick = () => dispatch(logout());

const isAuthenticated = useAppSelector(selectIsUserAuthenticated);
const isSessionBlocked = useAppSelector(selectIsSessionBlocked);

const { canRunWebRTC, isChromeBased, isFirefoxBased } = useAgentHelper();

Expand Down Expand Up @@ -92,7 +97,7 @@ export default function HomeTemplate({
pb: 1,
}}
>
{isAuthenticated && (
{isAuthenticated && !isSessionBlocked && (
<IconButton
aria-label="Logout"
title="Logout"
Expand All @@ -103,13 +108,15 @@ export default function HomeTemplate({
<LogoutIcon />
</IconButton>
)}
<IconButton
aria-label="Settings"
title="Settings"
onClick={openSettings}
>
<SettingsIcon />
</IconButton>
{!isSessionBlocked && (
<IconButton
aria-label="Settings"
title="Settings"
onClick={openSettings}
>
<SettingsIcon />
</IconButton>
)}
</Box>
</Box>
{!canRunWebRTC() && (
Expand Down
10 changes: 10 additions & 0 deletions src/features/auth/BlockedSession.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "../../testing-helpers/mock-firestore-auth";
import { act } from "react-dom/test-utils";
import fullRender from "../../testing-helpers/fullRender";
import BlockedSession from "./BlockedSession";

describe("BlockedSession", () => {
it("renders", async () => {
await act(() => fullRender(<BlockedSession />));
});
});
21 changes: 21 additions & 0 deletions src/features/auth/BlockedSession.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Box from "@mui/material/Box";
import HomeTemplate from "../../components/templates/HomeTemplate";
import useRedirectionRule from "../../hooks/useRedirectionRule";
import { Redirect } from "wouter";
import ErrorAlert from "../../components/basic/ErrorAlert";

export default function BlockedSession() {
const goTo = useRedirectionRule();

if (goTo) {
return <Redirect to={goTo} />;
}

return (
<HomeTemplate>
<Box component="span" pr={1}>
<ErrorAlert message="Account in use in another device. Reload to restore this session." />
</Box>
</HomeTemplate>
);
}
129 changes: 110 additions & 19 deletions src/hooks/useRedirectionRule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@ import { getRedirectionRule } from "./useRedirectionRule";

describe("getRedirectionRule: / (auth)", () => {
it("when authenticated, go to creation", () => {
const result = getRedirectionRule({ path: "/", hasAuth: true }, {});
const result = getRedirectionRule(
{ path: "/", hasAuth: true, isSessionBlocked: false },
{}
);
expect(result).toBe("/create");
});

it("when not authenticated, stay here for auth", () => {
const result = getRedirectionRule({ path: "/", hasAuth: false }, {});
const result = getRedirectionRule(
{ path: "/", hasAuth: false, isSessionBlocked: false },
{}
);
expect(result).toBe("");
});

it("when autenticated and provided joining uid, then go to join page proceed to device testing", () => {
const result = getRedirectionRule(
{ path: "/", hasAuth: true },
{ path: "/", hasAuth: true, isSessionBlocked: false },
{ joining: "123-321" }
);
expect(result).toBe("/join?callUid=123-321");
});

it("when autenticated and provided creating display name, then go to create page proceed to device testing", () => {
const result = getRedirectionRule(
{ path: "/", hasAuth: true },
{ path: "/", hasAuth: true, isSessionBlocked: false },
{ creating: "Daily" }
);
expect(result).toBe("/create?callDisplayName=Daily"); // TODO: names with special characters?
Expand All @@ -30,100 +36,177 @@ describe("getRedirectionRule: / (auth)", () => {

describe("getRedirectionRule: /create", () => {
it("when not authenticated, then go back to root for login", () => {
const result = getRedirectionRule({ path: "/create", hasAuth: false }, {});
const result = getRedirectionRule(
{ path: "/create", hasAuth: false, isSessionBlocked: false },
{}
);
expect(result).toBe("/");
});

it("when authenticated, stay here for device testing and call creation", () => {
const result = getRedirectionRule({ path: "/create", hasAuth: true }, {});
const result = getRedirectionRule(
{ path: "/create", hasAuth: true, isSessionBlocked: false },
{}
);
expect(result).toBe("");
});

it("when authenticated and has ongoing call, go to its page", () => {
const result = getRedirectionRule(
{ path: "/create", hasAuth: true, ongoingCall: "123-321" },
{
path: "/create",
hasAuth: true,
ongoingCall: "123-321",
isSessionBlocked: false,
},
{}
);
expect(result).toBe("/p2p-call/123-321");
});

it("when session is blocked, got to blocked session page", () => {
const result = getRedirectionRule(
{
path: "/create",
hasAuth: true,
ongoingCall: "123-321",
isSessionBlocked: true,
},
{}
);
expect(result).toBe("/blocked-session");
});
});

describe("getRedirectionRule: /join", () => {
it("when authenticated and provided call uid, then stay here for device testing", () => {
const result = getRedirectionRule(
{ path: "/join", hasAuth: true },
{ path: "/join", hasAuth: true, isSessionBlocked: false },
{ callUid: "123-321" }
);
expect(result).toBe("");
});

it("when not authenticated but provided call uid, then go authenticate", () => {
const result = getRedirectionRule(
{ path: "/join", hasAuth: false },
{ path: "/join", hasAuth: false, isSessionBlocked: false },
{ callUid: "123-321" }
);
expect(result).toBe("/?joining=123-321");
});

it("when not authenticated nor provided call uid, then start reset entire flow (came here by mistake?)", () => {
const result = getRedirectionRule({ path: "/join", hasAuth: false }, {});
const result = getRedirectionRule(
{ path: "/join", hasAuth: false, isSessionBlocked: false },
{}
);
expect(result).toBe("/");
});

it("when authenticated and didnt provide call uid, then stay to manually type it", () => {
const result = getRedirectionRule({ path: "/join", hasAuth: true }, {});
const result = getRedirectionRule(
{ path: "/join", hasAuth: true, isSessionBlocked: false },
{}
);
expect(result).toBe("");
});

it("when authenticated and has pending call, go to pending user page", () => {
const result = getRedirectionRule(
{ path: "/join", hasAuth: true, pendingCall: "123-call-321" },
{
path: "/join",
hasAuth: true,
pendingCall: "123-call-321",
isSessionBlocked: false,
},
{}
);
expect(result).toBe("/pending");
});

it("when session is blocked, got to blocked session page", () => {
const result = getRedirectionRule(
{
path: "/join",
hasAuth: true,
pendingCall: "123-call-321",
isSessionBlocked: true,
},
{}
);
expect(result).toBe("/blocked-session");
});
});

describe("getRedirectionRule: /join", () => {
it("if has pending call, stay waiting", () => {
const result = getRedirectionRule(
{ path: "/pending", hasAuth: true, pendingCall: "123-call-321" },
{
path: "/pending",
hasAuth: true,
pendingCall: "123-call-321",
isSessionBlocked: false,
},
{}
);
expect(result).toBe("");
});

it("if has ongoing call, go for it", () => {
const result = getRedirectionRule(
{ path: "/pending", hasAuth: true, ongoingCall: "123-call-321" },
{
path: "/pending",
hasAuth: true,
ongoingCall: "123-call-321",
isSessionBlocked: false,
},
{}
);
expect(result).toBe("/p2p-call/123-call-321");
});

it("if has none, reset navigation flow", () => {
const result = getRedirectionRule({ path: "/pending", hasAuth: true }, {});
const result = getRedirectionRule(
{ path: "/pending", hasAuth: true, isSessionBlocked: false },
{}
);
expect(result).toBe("/");
});

it("when not authenticated, reset navigation flow", () => {
const result = getRedirectionRule({ path: "/pending", hasAuth: false }, {});
const result = getRedirectionRule(
{ path: "/pending", hasAuth: false, isSessionBlocked: false },
{}
);
expect(result).toBe("/");
});

it("when session is blocked, got to blocked session page", () => {
const result = getRedirectionRule(
{ path: "/pending", hasAuth: true, isSessionBlocked: true },
{}
);
expect(result).toBe("/blocked-session");
});
});

describe("getRedirectionRule: /p2p-call", () => {
it("when authenticated with ongoing call, then stay here", () => {
const result = getRedirectionRule(
{ path: "/p2p-call/123-321", hasAuth: true, ongoingCall: "123-321" },
{
path: "/p2p-call/123-321",
hasAuth: true,
ongoingCall: "123-321",
isSessionBlocked: false,
},
{}
);
expect(result).toBe("");
});

it("when authenticated without ongoing call, go to a page saying good bye", () => {
const result = getRedirectionRule(
{ path: "/p2p-call/123-321", hasAuth: true },
{ path: "/p2p-call/123-321", hasAuth: true, isSessionBlocked: false },
{}
);
// expect(result).toBe("/left");
Expand All @@ -133,9 +216,17 @@ describe("getRedirectionRule: /p2p-call", () => {
it("when not authenticated, be forced to reset flow", () => {
// TODO: retrieve attempt call uid and convert into "/?joining=" flow
const result = getRedirectionRule(
{ path: "/p2p-call/123-321", hasAuth: false },
{ path: "/p2p-call/123-321", hasAuth: false, isSessionBlocked: false },
{}
);
expect(result).toBe("/");
});

it("when session is blocked, got to blocked session page", () => {
const result = getRedirectionRule(
{ path: "/p2p-call/123-321", hasAuth: false, isSessionBlocked: true },
{}
);
expect(result).toBe("/blocked-session");
});
});
Loading