Skip to content

Commit

Permalink
Merge pull request #8 from OurFreeLight/dev
Browse files Browse the repository at this point in the history
0.4.6 merge
  • Loading branch information
ncoonrod authored Jan 23, 2024
2 parents 6f46bbe + ecde79b commit 494f57c
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 78 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@hotstaq/userroute",
"description": "A user route for HotStaq. Allows users to be created/edited/deleted securely.",
"version": "0.4.3",
"version": "0.4.6",
"main": "build/src/index.js",
"scripts": {
"test": "hotstaq --dev --env-file .env run --server-type api --api-test",
Expand Down
215 changes: 167 additions & 48 deletions src/AdminRoute.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,120 @@
import * as ppath from "path";

import { HotRoute, ServerRequest, HotTestDriver, HotStaq, HotServerType, HotDBMySQL, ConnectionStatus, HotAPI } from "hotstaq";
import { IJWTToken, IUser, User } from "./User";
import { UserRoute } from "./UserRoute";
import { PassType } from "hotstaq/build/src/HotRouteMethod";
import { HotRouteMethodParameter, PassType } from "hotstaq/build/src/HotRouteMethod";

/**
* Admin route.
*/
export class AdminRoute extends UserRoute
{
/**
* Requires that each method requires authentication from a user of type.
* Set this to an empty string if you want to handle authentication yourself.
* Be warned, if you do this, you will need to check the user's permissions
* on EVERY method within this route.
*
* @default admin
*/
methodsRequireAuthType: string;
/**
* The database connection.
*/
db: HotDBMySQL;

constructor (api: HotAPI)
constructor (api: HotAPI, routeName: string = "admins")
{
super (api, "admins");

this.addMethod ({
"name": "listUsers",
"onServerExecute": this.listUsers,
"description": `Lists all users.`,
"parameters": {
"offset": {
"type": "int",
"required": false
},
"limit": {
"type": "int",
"required": false
}
},
"returns": "Returns the list of users.",
"testCases": [
"listUsersTest",
async (driver: HotTestDriver): Promise<any> =>
{
// @ts-ignore
let resp = await api.admins.listUsers ();
super (api, routeName);

this.methodsRequireAuthType = "admin";

driver.assert (resp.length > 0, "No users were returned.");
}
]
});
this.onPreRegister = async () =>
{
let userObjectDesc: HotRouteMethodParameter = {
"type": "object",
"description": "The user object.",
// @ts-ignore
"parameters": await HotStaq.convertInterfaceToRouteParameters (ppath.normalize (`${__dirname}/../../src/User.ts`), "IUser")
};

this.addMethod ({
"name": "editUser",
"onServerExecute": this.editUser,
"description": `Edit a user. The id set in the user object that is passed will be the id of the user that is edited.`,
"parameters": {
"user": userObjectDesc
},
"returns": "Returns true if the user was edited.",
"testCases": [
"editUserTest",
async (driver: HotTestDriver): Promise<any> =>
{
}
]
});
this.addMethod ({
"name": "deleteUser",
"onServerExecute": this.deleteUser,
"description": `Delete a user. The id set in the user object that is passed will be the id of the user that is deleted.`,
"parameters": {
"user": userObjectDesc
},
"returns": "Returns true if the user was deleted.",
"testCases": [
"deleteUserTest",
async (driver: HotTestDriver): Promise<any> =>
{
}
]
});
this.addMethod ({
"name": "changePassword",
"onServerExecute": this.changePassword,
"description": `Change a user's password. The id set in the user object that is passed will be the id of the user that has it's password changed.`,
"parameters": {
"user": userObjectDesc,
"newPassword": {
"type": "string",
"description": "The new password to set."
}
},
"returns": "Returns true if the user's password was changed.",
"testCases": [
"changePasswordTest",
async (driver: HotTestDriver): Promise<any> =>
{
}
]
});
this.addMethod ({
"name": "listUsers",
"onServerExecute": this.listUsers,
"description": `Lists all users.`,
"parameters": {
"offset": {
"type": "int",
"required": false
},
"limit": {
"type": "int",
"required": false
}
},
"returns": "Returns the list of users.",
"testCases": [
"listUsersTest",
async (driver: HotTestDriver): Promise<any> =>
{
// @ts-ignore
let resp = await api.admins.listUsers ();

driver.assert (resp.length > 0, "No users were returned.");
}
]
});
};
}

/**
Expand All @@ -55,39 +127,86 @@ export class AdminRoute extends UserRoute
}

/**
* The admin login. This checks to verify the user is an admin.
* Check a user's authentication.
*/
protected async checkAuth (req: ServerRequest): Promise<void>
{
if (this.methodsRequireAuthType !== "")
{
const jwtToken: string = HotStaq.getParam ("jwtToken", req.jsonObj);
const decoded: IJWTToken = await User.decodeJWTToken (jwtToken);
const authUser: IUser = decoded.user;

if (authUser.userType !== this.methodsRequireAuthType)
throw new Error (`Only user of type ${this.methodsRequireAuthType} is allowed to use this method.`);
}
}

/**
* The admin login. This checks to verify the user is of the user type set in
* this.methodsRequireAuthType.
*/
protected async login (req: ServerRequest): Promise<any>
{
let user: User = await super.login (req);

if (user.userType !== "admin")
throw new Error (`Only admins are allowed to login to this route.`);
await this.checkAuth (req);

return (user);
}

/**
* Edit a user.
*/
protected async editUser (req: ServerRequest): Promise<boolean>
{
await this.checkAuth (req);

const userObj: IUser = HotStaq.getParam ("user", req.jsonObj);
const user: User = new User (userObj);

await User.editUser (this.db, user);

return (true);
}

/**
* Delete a user.
*/
protected async deleteUser (req: ServerRequest): Promise<boolean>
{
await this.checkAuth (req);

const userObj: IUser = HotStaq.getParam ("user", req.jsonObj);
const user: User = new User (userObj);

await User.deleteUser (this.db, user);

return (true);
}

/**
* Change a user's password.
*/
protected async changePassword (req: ServerRequest): Promise<any>
{
await this.checkAuth (req);

const userObj: IUser = HotStaq.getParam ("user", req.jsonObj);
const user: User = new User (userObj);
const newPassword: string = HotStaq.getParam ("newPassword", req.jsonObj);

await User.changePassword (this.db, user, newPassword);

return (true);
}

/**
* List users.
*
* **WARNING:** By default, this method can be used by anyone. To
* prevent this, use "onServerPreExecute" to check the user's permissions.
* For better performance, be sure to use User.decodeJWTToken in onServerPreExecute
* to decode the JWT token and store the user in req.passObject.jsonObj. Without
* this, the user will be decoded twice.
*/
protected async listUsers (req: ServerRequest): Promise<any>
{
let user: IUser = null;

if (req.passObject.passType === PassType.Update)
user = req.passObject.jsonObj;
else
{
const jwtToken: string = HotStaq.getParam ("jwtToken", req.jsonObj);
let decoded: IJWTToken = await User.decodeJWTToken (jwtToken);
user = decoded.user;
}
await this.checkAuth (req);

const search: string = HotStaq.getParamDefault ("search", req.jsonObj, null);
const offset: number = HotStaq.getParamDefault ("offset", req.jsonObj, 0);
Expand Down
Loading

0 comments on commit 494f57c

Please sign in to comment.