diff --git a/src/controllers/GroupsController.ts b/src/controllers/GroupsController.ts index 84042e6..1dd4fdc 100644 --- a/src/controllers/GroupsController.ts +++ b/src/controllers/GroupsController.ts @@ -1,5 +1,6 @@ import { RequestHandler } from "../entities/RequestHandler"; import { GroupsHelper } from "../helpers/GroupsHelper"; +import { GroupsAccessLevel } from "../entities/Group"; /** * Groups API controller @@ -18,4 +19,13 @@ export class GroupsController { h.send(await GroupsHelper.GetListUser(h.getUserId())); } + /** + * Get information about a single group + * + * @param h Request handler + */ + public static async GetInfoSingle(h: RequestHandler) { + const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.LIMITED_ACCESS); + h.send("Good progress!"); + } } \ No newline at end of file diff --git a/src/controllers/Routes.ts b/src/controllers/Routes.ts index 992f6a2..aaaffc0 100644 --- a/src/controllers/Routes.ts +++ b/src/controllers/Routes.ts @@ -86,4 +86,6 @@ export const Routes : Route[] = [ // Groups controller {path: "/groups/get_my_list", cb: (h) => GroupsController.GetListUser(h)}, + + {path: "/groups/get_info", cb: (h) => GroupsController.GetInfoSingle(h)}, ] \ No newline at end of file diff --git a/src/entities/Group.ts b/src/entities/Group.ts new file mode 100644 index 0000000..715a39e --- /dev/null +++ b/src/entities/Group.ts @@ -0,0 +1,26 @@ +/** + * Single group information + * + * @author Pierre HUBERT + */ + +/** + * Group visibility level + */ +export enum GroupVisibilityLevel { + OPEN_GROUP = 0, + PRIVATE_GROUP = 1, + SECRETE_GROUP = 2 +} + +/** + * Access level of a user to a group + */ +export enum GroupsAccessLevel { + NO_ACCESS = 0, //Can not even know if the group exists or not + LIMITED_ACCESS = 1, //Access to the name of the group only + VIEW_ACCESS = 2, //Can see the posts of the group, but not a member of the group + MEMBER_ACCESS = 3, //Member access (same as view access but as member) + MODERATOR_ACCESS = 4, //Can create posts, even if posts creation is restricted + ADMIN_ACCESS = 5, //Can do everything +} \ No newline at end of file diff --git a/src/entities/GroupMember.ts b/src/entities/GroupMember.ts new file mode 100644 index 0000000..582ef3e --- /dev/null +++ b/src/entities/GroupMember.ts @@ -0,0 +1,14 @@ +/** + * Group membership information + * + * @author Pierre HUBERT + */ + +export enum GroupMembershipLevels { + ADMINISTRATOR = 0, + MODERATOR = 1, + MEMBER = 2, + INVITED = 3, + PENDING = 4, //When the group membership has not been approved yet + VISITOR = 5, //Simple visitor +} \ No newline at end of file diff --git a/src/entities/RequestHandler.ts b/src/entities/RequestHandler.ts index b297eab..8baf22b 100644 --- a/src/entities/RequestHandler.ts +++ b/src/entities/RequestHandler.ts @@ -7,6 +7,8 @@ import { UploadedFile } from "express-fileupload"; import { prepareFileCreation, generateNewUserDataFileName, pathUserData } from "../utils/UserDataUtils"; import * as sharp from 'sharp'; import { UserHelper } from "../helpers/UserHelper"; +import { GroupsAccessLevel } from "./Group"; +import { GroupsHelper } from "../helpers/GroupsHelper"; /** * Response to a request @@ -196,6 +198,41 @@ export class RequestHandler { return userID; } + /** + * Get a POST group ID + * + * @param name The name of the POST field + */ + public async postGroupID(name: string) : Promise { + const groupID = this.postInt(name); + + if(!await GroupsHelper.Exists(groupID)) + this.error(404, "Specified group not found!"); + + return groupID; + } + + /** + * Get a POST group ID with a check for access level of current user + * + * @param name The name of the POST field containing group ID + * @param minVisibility Minimum visiblity requested to the group + * @returns The ID of the group (throws in case of failure) + */ + public async postGroupIDWithAccess(name: string, minVisibility : GroupsAccessLevel) : Promise { + const groupID = await this.postGroupID(name); + + const access = await GroupsHelper.GetAccessLevel(groupID, this.getUserId()); + + if(access == GroupsAccessLevel.NO_ACCESS) + this.error(404, "Specified group not found!"); + + if(access < minVisibility) + this.error(401, "You do not have enough rights to perform what you intend to do on this group!"); + + return groupID; + } + /** * Get information about an uploaded file * diff --git a/src/helpers/GroupsHelper.ts b/src/helpers/GroupsHelper.ts index 04cf80f..486ad57 100644 --- a/src/helpers/GroupsHelper.ts +++ b/src/helpers/GroupsHelper.ts @@ -1,4 +1,6 @@ import { DatabaseHelper } from "./DatabaseHelper"; +import { GroupsAccessLevel, GroupVisibilityLevel } from "../entities/Group"; +import { GroupMembershipLevels } from "../entities/GroupMember"; /** * Groups helper @@ -11,6 +13,20 @@ const GROUPS_MEMBERS_TABLE = "comunic_groups_members"; export class GroupsHelper { + /** + * Check out whether a group exists or not + * + * @param groupID Target group ID + */ + public static async Exists(groupID: number) : Promise { + return await DatabaseHelper.Count({ + table: GROUPS_LIST_TABLE, + where: { + id: groupID + } + }) > 0; + } + /** * Get the list of groups of a user * @@ -28,4 +44,86 @@ export class GroupsHelper { return list.map(e => e.groups_id); } + /** + * Get the visibility level of a group + * + * @param groupID Target group ID + */ + public static async GetVisibility(groupID: number) : Promise { + const result = await DatabaseHelper.QueryRow({ + table: GROUPS_LIST_TABLE, + where: { + id: groupID + }, + fields: ["visibility"] + }); + + if(result == null) + throw Error("Group " + groupID + " does not exists!"); + + return result.visibility; + } + + /** + * Get the membership level of a user for a group + * + * @param groupID The ID of target group + * @param userID The ID of target user + */ + public static async GetMembershipLevel(groupID: number, userID: number) : Promise { + const result = await DatabaseHelper.Query({ + table: GROUPS_MEMBERS_TABLE, + where: { + groups_id: groupID, + user_id: userID + }, + fields: ["level"] + }); + + // If user has no membership + if(result.length == 0) + return GroupMembershipLevels.VISITOR; + + return result[0].level; + + } + + /** + * Get the current access of a user to a group + * + * @param groupID The ID of the target group + * @param userID The ID of the target user + */ + public static async GetAccessLevel(groupID: number, userID: number) : Promise { + + const membershipLevel = + userID > 0 ? await this.GetMembershipLevel(userID, groupID) : GroupMembershipLevels.VISITOR; + + //Check if the user is a confirmed member of group + if(membershipLevel == GroupMembershipLevels.ADMINISTRATOR) + return GroupsAccessLevel.ADMIN_ACCESS; + if(membershipLevel == GroupMembershipLevels.MODERATOR) + return GroupsAccessLevel.MODERATOR_ACCESS; + if(membershipLevel == GroupMembershipLevels.MEMBER) + return GroupsAccessLevel.MEMBER_ACCESS; + + const groupVisibilityLevel = await this.GetVisibility(groupID); + + //If the group is open, everyone has view access + if(groupVisibilityLevel == GroupVisibilityLevel.OPEN_GROUP) + return GroupsAccessLevel.VIEW_ACCESS; + + //Else, all pending and invited membership get limited access + if(membershipLevel == GroupMembershipLevels.PENDING || + membershipLevel == GroupMembershipLevels.INVITED) + return GroupsAccessLevel.LIMITED_ACCESS; + + //Private groups gives limited access + if(groupVisibilityLevel == GroupVisibilityLevel.PRIVATE_GROUP) + return GroupsAccessLevel.LIMITED_ACCESS; + + // Else the user can not see the group + // Especially in the case of secret groupe + return GroupsAccessLevel.NO_ACCESS; + } } \ No newline at end of file