Change db table name + Modify unit tests + Update README

This commit is contained in:
AbegaM
2024-03-13 14:33:33 +03:00
parent 474c25c34d
commit fd62c9781e
7 changed files with 117 additions and 38 deletions

View File

@@ -11,11 +11,12 @@ RATE_LIMIT_MAX_REQUESTS=10
DB=foobar.db
INITIAL_USER_USERNAME=<your_username>
INITIAL_USER_PASSWORD=<your_password>
TOKEN_SECRET=ABCD23DCAA
ACCESS_TOKEN_EXPIRATION_TIME=10H
REFRESH_TOKEN_EXPIRATION_TIME=2D
INITIAL_USER_USERNAME
INITIAL_USER_PASSWORD
TOKEN_SECRET
START_WITH_STUDIO=false

View File

@@ -29,22 +29,22 @@ Usage: soul [options]
Options:
--version Show version number [boolean]
-d, --database SQLite database file or :memory: [string] [required]
-p, --port Port to listen on [number]
-r, --rate-limit-enabled Enable rate limiting [boolean]
-c, --cors CORS whitelist origins [string]
-a, --auth Enable authentication and authorization [boolean]
--version Show version number [boolean]
-d, --database SQLite database file or :memory: [string] [required]
-p, --port Port to listen on [number]
-r, --rate-limit-enabled Enable rate limiting [boolean]
-c, --cors CORS whitelist origins [string]
-a, --auth Enable authentication and authorization [boolean]
-iuu, --initialuserusername Initial user username [string]
-iup, --initialuserpassword Initial user password [string]
-iuu, --initialuserusername Initial user username [string]
-iup, --initialuserpassword Initial user password [string]
-ats, --accesstokensecret Access Token Secret [string]
-atet, --accesstokenexpirationtime Access Token Expiration Time [string]
-rts, --refreshtokensecret Refresh Token Secret [string]
-rtet, --refreshtokenexpirationtime Refresh Token Expiration Time [string]
-S, --studio Start Soul Studio in parallel
--help Show help
-ats, --accesstokensecret Access Token Secret [string]
-atet, --accesstokenexpirationtime Access Token Expiration Time (Default: 5H) [string]
-rts, --refreshtokensecret Refresh Token Secret [string]
-rtet, --refreshtokenexpirationtime Refresh Token Expiration Time (Default: 1D) [string]
-S, --studio Start Soul Studio in parallel
--help Show help
```
@@ -62,12 +62,11 @@ To run Soul in auth mode, allowing login and signup features with authorization
Run the Soul command with the necessary parameters:
```
```
soul --d foobar.db -a -ts <your_jwt_secret_value> -atet=4H -rtet=3D -iuu=john -iup=<your_password>
soul --d foobar.db -a -ts <your_jwt_secret_value> -atet=4H -rtet=3D -iuu=john -iup=<your_password>
```
```
Note: When configuring your JWT Secret, it is recommended to use a long string value for enhanced security. It is advisable to use a secret that is at least 10 characters in length.

View File

@@ -4,7 +4,7 @@ const { updateSuperuser } = require('./controllers/auth');
const { argv } = yargs;
const runCLICommands = () => {
//If the updatesuperuser command is passed from the CLI execute the updatesuperuser function
// if the updatesuperuser command is passed from the CLI execute the updatesuperuser function
if (argv._.includes('updatesuperuser')) {
const { id, password, is_superuser } = argv;

View File

@@ -8,6 +8,6 @@ module.exports = {
USER_TABLE: '_users',
ROLE_TABLE: '_roles',
USER_ROLES_TABLE: '_users_roles',
USERS_ROLES_TABLE: '_users_roles',
ROLE_PERMISSIONS_TABLE: '_roles_permissions',
};

View File

@@ -27,13 +27,11 @@ const createDefaultTables = async () => {
// create _users table
if (!usersTable) {
// create the _users table
tableService.createTable(USER_TABLE, schema.userSchema);
}
// create _users_roles table
if (!usersRolesTable) {
// create the _users_roles table
tableService.createTable(
USERS_ROLES_TABLE,
@@ -49,7 +47,6 @@ const createDefaultTables = async () => {
// create _roles table
if (!roleTable) {
// create the _role table
tableService.createTable(ROLE_TABLE, schema.roleSchema);
// create a default role in the _roles table
@@ -62,7 +59,6 @@ const createDefaultTables = async () => {
// create _roles_permissions table
if (!rolesPermissionTable && roleId) {
// create the _roles_permissions table
tableService.createTable(
ROLE_PERMISSIONS_TABLE,
schema.rolePermissionSchema,

View File

@@ -28,8 +28,13 @@ describe('Auth Endpoints', () => {
expect(res.status).toEqual(201);
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Row Inserted');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('POST /tables/_users/rows should throw 400 error if username is not passed', async () => {
@@ -48,6 +53,10 @@ describe('Auth Endpoints', () => {
expect(res.status).toEqual(400);
expect(res.body.message).toBe('username is required');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('POST /tables/_users/rows should throw 400 error if the password is not strong', async () => {
@@ -71,6 +80,10 @@ describe('Auth Endpoints', () => {
expect(res.body.message).toBe(
'This password is weak, please use another password',
);
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('POST /tables/_users/rows should throw 409 error if the username is taken', async () => {
@@ -92,6 +105,10 @@ describe('Auth Endpoints', () => {
expect(res.status).toEqual(409);
expect(res.body.message).toBe('This username is taken');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('GET /tables/_users/rows should return list of users', async () => {
@@ -110,6 +127,10 @@ describe('Auth Endpoints', () => {
expect(res.body.data[0]).toHaveProperty('username');
expect(res.body.data[0]).toHaveProperty('is_superuser');
expect(res.body.data[0]).toHaveProperty('createdAt');
expect(res.body.data[0]).not.toHaveProperty('password');
expect(res.body.data[0]).not.toHaveProperty('hashed_password');
expect(res.body.data[0]).not.toHaveProperty('salt');
});
it('GET /tables/_users/rows/:id should retrive a single user', async () => {
@@ -128,6 +149,10 @@ describe('Auth Endpoints', () => {
expect(res.body.data[0]).toHaveProperty('username');
expect(res.body.data[0]).toHaveProperty('is_superuser');
expect(res.body.data[0]).toHaveProperty('createdAt');
expect(res.body.data[0]).not.toHaveProperty('password');
expect(res.body.data[0]).not.toHaveProperty('hashed_password');
expect(res.body.data[0]).not.toHaveProperty('salt');
});
it('PUT /tables/_users/rows/:id should update a user', async () => {
@@ -147,6 +172,14 @@ describe('Auth Endpoints', () => {
});
expect(res.status).toEqual(200);
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Row updated');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('PUT /tables/_users/rows/:id should throw a 409 error if the username is taken', async () => {
@@ -167,6 +200,10 @@ describe('Auth Endpoints', () => {
expect(res.status).toEqual(409);
expect(res.body.message).toEqual('This username is already taken');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('DELETE /tables/_users/rows/:id should remove a user', async () => {
@@ -182,6 +219,10 @@ describe('Auth Endpoints', () => {
expect(res.status).toEqual(400);
expect(res.body.message).toBe('FOREIGN KEY constraint failed');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
});
@@ -200,6 +241,18 @@ describe('Auth Endpoints', () => {
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Success');
expect(res.headers['set-cookie']).toBeDefined();
expect(res.headers['set-cookie']).toEqual(
expect.arrayContaining([
expect.stringContaining('refreshToken='),
expect.stringContaining('accessToken='),
]),
);
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('POST /auth/token/obtain should throw a 401 error if the username does not exist in the DB', async () => {
@@ -216,6 +269,10 @@ describe('Auth Endpoints', () => {
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Invalid username or password');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
it('POST /auth/token/obtain should throw a 401 error if the password is invalid', async () => {
@@ -232,17 +289,15 @@ describe('Auth Endpoints', () => {
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Invalid username or password');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
});
describe('Refresh Access Token Endpoint', () => {
it('GET /auth/token/refresh should refresh the access and refresh tokens', async () => {
const accessToken = await generateToken(
{ username: 'John', userId: 1, isSuperuser: true },
config.tokenSecret,
'1H',
);
const refreshToken = await generateToken(
{ username: 'John', userId: 1, isSuperuser: true },
config.tokenSecret,
@@ -251,15 +306,20 @@ describe('Auth Endpoints', () => {
const res = await requestWithSupertest
.get('/api/auth/token/refresh')
.set('Cookie', [
`accessToken=${accessToken}`,
`refreshToken=${refreshToken}`,
]);
.set('Cookie', [`refreshToken=${refreshToken}`]);
expect(res.status).toEqual(200);
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Success');
expect(res.headers['set-cookie']).toBeDefined();
expect(res.headers['set-cookie']).toEqual(
expect.arrayContaining([
expect.stringContaining('refreshToken='),
expect.stringContaining('accessToken='),
]),
);
});
});
@@ -285,6 +345,25 @@ describe('Auth Endpoints', () => {
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Password updated successfully');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
// check if the password is really updated
const res2 = await requestWithSupertest
.post('/api/auth/token/obtain')
.send({
fields: {
username: testData.users.user1.username,
password: testData.strongPassword2,
},
});
expect(res2.status).toEqual(201);
expect(res2.type).toEqual(expect.stringContaining('json'));
expect(res2.body).toHaveProperty('message');
expect(res2.body.message).toBe('Success');
});
it('PUT /auth/change-password/ should throw 401 error if the current password is not valid', async () => {
@@ -308,6 +387,10 @@ describe('Auth Endpoints', () => {
expect(res.type).toEqual(expect.stringContaining('json'));
expect(res.body).toHaveProperty('message');
expect(res.body.message).toBe('Invalid current password');
expect(res.body).not.toHaveProperty('password');
expect(res.body).not.toHaveProperty('hashed_password');
expect(res.body).not.toHaveProperty('salt');
});
});
});

View File

@@ -1,7 +1,7 @@
{
"swagger": "2.0",
"info": {
"version": "0.6.1",
"version": "0.7.0",
"title": "Soul API",
"description": "API Documentation for <b>Soul</b>, a SQLite REST and realtime server. "
},