Add token valification unit test

This commit is contained in:
Takeshi Kimata
2024-06-01 13:52:46 +09:00
parent 39d5ab16a6
commit 09a7e08667
5 changed files with 221 additions and 59 deletions

View File

@@ -6,7 +6,7 @@ export type UserType = {
password: string;
username: string;
role: number;
avatarPath: string;
avatarPath: string | null;
} | null;
export type TokenProps = {

View File

@@ -3,7 +3,11 @@ import { createContext, useState, useEffect, useContext } from 'react';
import { TokenContextType, TokenType } from '@/types/user';
import { TokenProps } from '@/types/user';
import { useRouter, usePathname } from '@/src/navigation';
import { roles } from '@/config/selection';
import {
isSignedIn as tokenIsSinedIn,
isAdmin as tokenIsAdmin,
checkSignInPage as tokenCheckSignInPage,
} from './token';
import { ToastContext } from './ToastProvider';
const LOCAL_STORAGE_KEY = 'testplat-auth-token';
@@ -40,39 +44,12 @@ const TokenProvider = ({ toastMessages, locale, children }: TokenProps) => {
user: null,
});
const tokenExists = () => {
if (token && token.user && token.user.username) {
return true;
} else {
return false;
}
};
const isTokenValid = () => {
if (Date.now() < token.expires_at) {
return true;
} else {
return false;
}
};
const isSignedIn = () => {
if (tokenExists() && isTokenValid()) {
return true;
} else {
return false;
}
return tokenIsSinedIn(token);
};
const isAdmin = () => {
if (tokenExists() && isTokenValid()) {
const adminRoleIndex = roles.findIndex((entry) => entry.uid === 'administrator');
if (token.user.role === adminRoleIndex) {
return true;
}
} else {
return false;
}
return tokenIsAdmin(token);
};
const tokenContext = {
@@ -98,32 +75,7 @@ const TokenProvider = ({ toastMessages, locale, children }: TokenProps) => {
}, []);
useEffect(() => {
// pravate paths are '/account', '/admin', '/projects/*'
const isPrivatePath = (pathname: string) => {
return (
/^\/account(\/)?$/.test(pathname) || /^\/admin(\/)?$/.test(pathname) || /^\/projects(\/.*)?$/.test(pathname)
);
};
const checkSignInPage = () => {
if (!hasRestoreFinished) {
return;
}
if (isPrivatePath(pathname) && !isSignedIn()) {
if (tokenExists()) {
toastContext.showToast(toastMessages.needSignedIn, 'error');
router.push(`/account/signin`, { locale: locale });
return;
}
if (isTokenValid()) {
toastContext.showToast(toastMessages.sessionExpired, 'error');
router.push(`/account/signin`, { locale: locale });
return;
}
}
};
checkSignInPage();
tokenCheckSignInPage(token, pathname);
}, [pathname, hasRestoreFinished]);
return <TokenContext.Provider value={tokenContext}>{children}</TokenContext.Provider>;

View File

@@ -0,0 +1,137 @@
import { describe, expect, test } from 'vitest';
import { isPrivatePath, checkSignInPage, isAdmin } from './token';
describe('check account path', () => {
test('"account/" is private', () => {
expect(isPrivatePath('/account')).toBe(true);
});
test('"account/signin" is public', () => {
expect(isPrivatePath('/account/signin')).toBe(false);
});
test('"account/signup" is public', () => {
expect(isPrivatePath('/account/signup')).toBe(false);
});
});
describe('check admin path', () => {
test('"admin/" is private', () => {
expect(isPrivatePath('/admin')).toBe(true);
});
test('"admin/something" is private', () => {
expect(isPrivatePath('/admin/signin')).toBe(true);
});
});
describe('check projects path', () => {
test('"projects/" is private', () => {
expect(isPrivatePath('/projects')).toBe(true);
});
test('"projects/1" is private', () => {
expect(isPrivatePath('/projects/1')).toBe(true);
});
});
const validAdminToken = {
access_token: 'loremipsumdolorsitametconsecteturadipiscingelit',
expires_at: Infinity,
user: {
id: 1,
email: 'admin@example.com',
password: 'hashedpassword',
username: 'admin',
role: 0,
avatarPath: null,
createdAt: '2024-06-01T00:18:03.859Z',
updatedAt: '2024-06-01T00:18:03.859Z',
},
};
const validUserToken = {
access_token: 'loremipsumdolorsitametconsecteturadipiscingelit',
expires_at: Infinity,
user: {
id: 2,
email: 'user@example.com',
password: 'hashedpassword',
username: 'user',
role: 1,
avatarPath: null,
createdAt: '2024-06-01T00:18:03.859Z',
updatedAt: '2024-06-01T00:18:03.859Z',
},
};
const expiredUserToken = {
access_token: 'loremipsumdolorsitametconsecteturadipiscingelit',
expires_at: 1717200000000,
user: {
id: 2,
email: 'user@example.com',
password: 'hashedpassword',
username: 'user',
role: 1,
avatarPath: null,
createdAt: '2024-06-01T00:18:03.859Z',
updatedAt: '2024-06-01T00:18:03.859Z',
},
};
describe('check user is admin or not', () => {
test('user with admin role is admin', () => {
expect(isAdmin(validAdminToken)).toBe(true);
});
test('user with user role is not admin', () => {
expect(isAdmin(validUserToken)).toBe(false);
});
});
describe('check sign in', () => {
test('Access to public paths by not signed user will be allowed', () => {
const token = {
access_token: '',
expires_at: 0,
user: null,
};
const privatePath = '/';
expect(checkSignInPage(token, privatePath)).toStrictEqual({
ok: true,
reason: '',
redirectPath: '',
});
});
test('Access to private paths by not signed user in will be redirected', () => {
const token = {
access_token: '',
expires_at: 0,
user: null,
};
const privatePath = '/account';
expect(checkSignInPage(token, privatePath)).toStrictEqual({
ok: false,
reason: 'notoken',
redirectPath: '/account/signin',
});
});
test('Access to private paths by signed in user will be allowed', () => {
const privatePath = '/account';
expect(checkSignInPage(validAdminToken, privatePath)).toStrictEqual({
ok: true,
reason: '',
redirectPath: '',
});
});
test('Access to private paths by user whose token expired will be redirected', () => {
const privatePath = '/account';
expect(checkSignInPage(expiredUserToken, privatePath)).toStrictEqual({
ok: false,
reason: 'expired',
redirectPath: '/account/signin',
});
});
});

69
frontend/utils/token.ts Normal file
View File

@@ -0,0 +1,69 @@
import { TokenType } from '@/types/user';
import { roles } from '@/config/selection';
function tokenExists(token: TokenType) {
if (token && token.user && token.user.username) {
return true;
} else {
return false;
}
}
function isTokenValid(token: TokenType) {
if (Date.now() < token.expires_at) {
return true;
} else {
return false;
}
}
function isSignedIn(token: TokenType): boolean {
if (tokenExists(token) && isTokenValid(token)) {
return true;
} else {
return false;
}
}
function isAdmin(token: TokenType) {
if (tokenExists(token) && isTokenValid(token)) {
const adminRoleIndex = roles.findIndex((entry) => entry.uid === 'administrator');
if (token.user.role === adminRoleIndex) {
return true;
}
}
return false;
}
// pravate paths are '/account', '/admin', '/projects/*'
const isPrivatePath = (pathname: string) => {
return /^\/account(\/)?$/.test(pathname) || /^\/admin(\/.*)?$/.test(pathname) || /^\/projects(\/.*)?$/.test(pathname);
};
function checkSignInPage(token: TokenType, pathname: string) {
let ret = {
ok: true,
reason: '',
redirectPath: '',
};
if (isPrivatePath(pathname) && !isSignedIn(token)) {
if (!tokenExists(token)) {
ret.ok = false;
ret.reason = 'notoken';
ret.redirectPath = '/account/signin';
return ret;
}
if (!isTokenValid(token)) {
ret.ok = false;
ret.reason = 'expired';
ret.redirectPath = '/account/signin';
return ret;
}
}
return ret;
}
export { isSignedIn, isAdmin, isPrivatePath, checkSignInPage };