From 0a6292f227f1981689a81eeae63850f913c47800 Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Fri, 14 Jul 2023 11:17:13 -0700 Subject: [PATCH] feat: post-mod and post-delete utils for ACL (#1119) --- .../src/utils/channel-permission.ts | 249 +++++++++--------- .../src/utils/channels/can-create-channel.ts | 4 +- .../src/utils/channels/can-modify-channel.ts | 9 +- .../src/utils/channels/can-post-to-channel.ts | 4 +- .../src/utils/posts/can-delete-post.ts | 35 +++ .../src/utils/posts/can-modify-post-status.ts | 9 +- .../src/utils/posts/can-modify-post.ts | 9 +- packages/discussions/src/utils/posts/index.ts | 29 +- .../test/utils/channel-permission.test.ts | 206 +++++---------- .../utils/channels/can-modify-channel.test.ts | 30 +-- .../test/utils/posts/can-delete-post.test.ts | 80 ++++++ .../posts/can-modify-post-status.test.ts | 22 +- .../test/utils/posts/can-modify-post.test.ts | 27 ++ .../test/utils/posts/index.test.ts | 79 +----- 14 files changed, 372 insertions(+), 420 deletions(-) create mode 100644 packages/discussions/src/utils/posts/can-delete-post.ts create mode 100644 packages/discussions/test/utils/posts/can-delete-post.test.ts diff --git a/packages/discussions/src/utils/channel-permission.ts b/packages/discussions/src/utils/channel-permission.ts index 30044086308..1c4babff9eb 100644 --- a/packages/discussions/src/utils/channel-permission.ts +++ b/packages/discussions/src/utils/channel-permission.ts @@ -29,10 +29,12 @@ export class ChannelPermission { private isChannelAclEmpty: boolean; private permissionsByCategory: PermissionsByAclCategoryMap; + private channelCreator: string; - constructor(channelAcl: IChannelAclPermission[]) { + constructor(channelAcl: IChannelAclPermission[], creator: string) { this.isChannelAclEmpty = channelAcl.length === 0; this.permissionsByCategory = {}; + this.channelCreator = creator; channelAcl.forEach((permission) => { const { category } = permission; @@ -42,7 +44,7 @@ export class ChannelPermission { } canPostToChannel(user: IDiscussionsUser) { - if (this.aclAllowsAnyUserToPost()) { + if (this.canAnyUserWrite()) { return true; } @@ -51,17 +53,13 @@ export class ChannelPermission { } return ( - this.aclAllowsAnyAuthenticatedUserToPost() || - this.aclAllowsThisUserToPost(user) || - this.aclAllowsThisUserToPostByGroups(user) || - this.aclAllowsThisUserToPostByOrg(user) + this.canAnyAuthenticatedUserWrite() || + this.isUserAWriteUser(user) || + this.isUserPartOfWriteGroup(user) || + this.isUserPartOfWriteOrg(user) ); } - canModifyPostStatus(user: IDiscussionsUser, channelCreator: string) { - return this.canModifyChannel(user, channelCreator); - } - canCreateChannel(user: IDiscussionsUser) { if (this.isUserUnAuthenticated(user) || this.isChannelAclEmpty) { return false; @@ -76,87 +74,109 @@ export class ChannelPermission { ); } - canModifyChannel(user: IDiscussionsUser, channelCreator: string) { + canModerateChannel(user: IDiscussionsUser) { if (this.isUserUnAuthenticated(user)) { return false; } - if (user.username === channelCreator) { - return true; - } - return ( - this.aclAllowsThisUserToModifyChannel(user) || - this.aclAllowsThisUserToModifyChannelByGroups(user) || - this.aclAllowsThisUserToModifyChannelByOrg(user) + user.username === this.channelCreator || + this.isUserAModeratorUser(user) || + this.isUserPartOfModeratorGroup(user) || + this.isUserPartOfModeratorOrg(user) ); } - private isAuthorizedToPost(role?: Role) { - return this.ALLOWED_ROLES_FOR_POSTING.includes(role); + /** + * canCreateChannelHelpers + */ + private userCanAddAnonymousToAcl(user: IDiscussionsUser) { + if (!this.permissionsByCategory[AclCategory.ANONYMOUS_USER]) { + return true; + } + return isOrgAdmin(user); } - private isAuthorizedToModerate(role: Role) { - return this.ALLOWED_ROLES_FOR_MODERATION.includes(role); + private userCanAddUnauthenticatedToAcl(user: IDiscussionsUser) { + if (!this.permissionsByCategory[AclCategory.AUTHENTICATED_USER]) { + return true; + } + return isOrgAdmin(user); } - private isUserUnAuthenticated(user: IDiscussionsUser) { - return user.username === null || user.username === undefined; - } + private userCanAddAllGroupsToAcl(user: IDiscussionsUser) { + const groupPermissions = this.permissionsByCategory[AclCategory.GROUP]; + const userGroupsById = this.mapUserGroupsById(user.groups); - private mapUserGroupsById(groups: IGroup[]) { - return groups.reduce((accum, userGroup) => { - accum[userGroup.id] = userGroup; - return accum; - }, {} as Record); + if (!groupPermissions) { + return true; + } + + return groupPermissions.every((permission) => { + const userGroup = userGroupsById[permission.key]; + + return ( + userGroup && + this.isMemberTypeAuthorized(userGroup) && + this.isGroupDiscussable(userGroup) + ); + }); } - private isMemberTypeAuthorized(userGroup: IGroup) { - const { - userMembership: { memberType }, - } = userGroup; - return this.ALLOWED_GROUP_MEMBER_TYPES.includes(memberType); + private userCanAddAllOrgsToAcl(user: IDiscussionsUser) { + const orgPermissions = this.permissionsByCategory[AclCategory.ORG]; + + if (!orgPermissions) { + return true; + } + + return ( + isOrgAdmin(user) && + this.isEveryPermissionForUserOrg(user.orgId, orgPermissions) + ); } - private isMemberTypeAdmin(userGroup: IGroup) { - const { - userMembership: { memberType }, - } = userGroup; - return this.ADMIN_GROUP_MEMBER_TYPES.includes(memberType); + private isEveryPermissionForUserOrg( + userOrgId: string, + orgPermissions: IChannelAclPermissionDefinition[] + ) { + return orgPermissions.every((permission) => { + const { key: orgId } = permission; + return userOrgId === orgId; + }); } - private isGroupDiscussable(userGroup: IGroup) { - const { typeKeywords = [] } = userGroup; - return !typeKeywords.includes(CANNOT_DISCUSS); + // for now user permissions are disabled on channel create + // since users are not notified and cannot opt out + private userCanAddUsersToAcl(user: IDiscussionsUser) { + const userPermissions = this.permissionsByCategory[AclCategory.USER]; + return !userPermissions; } - /** - * canPostToChannel helpers - */ - private aclAllowsAnyUserToPost() { + private canAnyUserWrite() { const role = this.permissionsByCategory[AclCategory.ANONYMOUS_USER]?.[0].role; - return this.isAuthorizedToPost(role); + return this.isAuthorizedToWritePost(role); } - private aclAllowsAnyAuthenticatedUserToPost() { + private canAnyAuthenticatedUserWrite() { const role = this.permissionsByCategory[AclCategory.AUTHENTICATED_USER]?.[0].role; - return this.isAuthorizedToPost(role); + return this.isAuthorizedToWritePost(role); } - private aclAllowsThisUserToPost(user: IDiscussionsUser) { + private isUserAWriteUser(user: IDiscussionsUser) { const userPermissions = this.permissionsByCategory[AclCategory.USER] ?? []; const username = user.username; return userPermissions.some((permission) => { const { role, key } = permission; - return key === username && this.isAuthorizedToPost(role); + return key === username && this.isAuthorizedToWritePost(role); }); } - private aclAllowsThisUserToPostByGroups(user: IDiscussionsUser) { + private isUserPartOfWriteGroup(user: IDiscussionsUser) { const groupPermissions = this.permissionsByCategory[AclCategory.GROUP] ?? []; const userGroupsById = this.mapUserGroupsById(user.groups); @@ -177,7 +197,8 @@ export class ChannelPermission { private canAnyGroupMemberPost(permission: IChannelAclPermission) { const { subCategory, role } = permission; return ( - subCategory === AclSubCategory.MEMBER && this.isAuthorizedToPost(role) + subCategory === AclSubCategory.MEMBER && + this.isAuthorizedToWritePost(role) ); } @@ -185,11 +206,11 @@ export class ChannelPermission { const { subCategory, role } = permission; return ( - subCategory === AclSubCategory.ADMIN && this.isAuthorizedToPost(role) + subCategory === AclSubCategory.ADMIN && this.isAuthorizedToWritePost(role) ); } - private aclAllowsThisUserToPostByOrg(user: IDiscussionsUser) { + private isUserPartOfWriteOrg(user: IDiscussionsUser) { const orgPermissions = this.permissionsByCategory[AclCategory.ORG] ?? []; const { orgId: userOrgId } = user; @@ -207,14 +228,50 @@ export class ChannelPermission { private canAnyOrgMemberPost(permission: IChannelAclPermission) { const { subCategory, role } = permission; return ( - subCategory === AclSubCategory.MEMBER && this.isAuthorizedToPost(role) + subCategory === AclSubCategory.MEMBER && + this.isAuthorizedToWritePost(role) ); } - /** - * canModifyChannel helpers - */ - private aclAllowsThisUserToModifyChannel(user: IDiscussionsUser) { + private isAuthorizedToWritePost(role?: Role) { + return this.ALLOWED_ROLES_FOR_POSTING.includes(role); + } + + private isUserUnAuthenticated(user: IDiscussionsUser) { + return user.username === null || user.username === undefined; + } + + private mapUserGroupsById(groups: IGroup[]) { + return groups.reduce((accum, userGroup) => { + accum[userGroup.id] = userGroup; + return accum; + }, {} as Record); + } + + private isMemberTypeAuthorized(userGroup: IGroup) { + const { + userMembership: { memberType }, + } = userGroup; + return this.ALLOWED_GROUP_MEMBER_TYPES.includes(memberType); + } + + private isMemberTypeAdmin(userGroup: IGroup) { + const { + userMembership: { memberType }, + } = userGroup; + return this.ADMIN_GROUP_MEMBER_TYPES.includes(memberType); + } + + private isGroupDiscussable(userGroup: IGroup) { + const { typeKeywords = [] } = userGroup; + return !typeKeywords.includes(CANNOT_DISCUSS); + } + + private isAuthorizedToModerate(role: Role) { + return this.ALLOWED_ROLES_FOR_MODERATION.includes(role); + } + + private isUserAModeratorUser(user: IDiscussionsUser) { const userPermissions = this.permissionsByCategory[AclCategory.USER] ?? []; const username = user.username; @@ -225,7 +282,7 @@ export class ChannelPermission { }); } - private aclAllowsThisUserToModifyChannelByGroups(user: IDiscussionsUser) { + private isUserPartOfModeratorGroup(user: IDiscussionsUser) { const groupPermissions = this.permissionsByCategory[AclCategory.GROUP] ?? []; const userGroupsById = this.mapUserGroupsById(user.groups); @@ -258,7 +315,7 @@ export class ChannelPermission { ); } - private aclAllowsThisUserToModifyChannelByOrg(user: IDiscussionsUser) { + private isUserPartOfModeratorOrg(user: IDiscussionsUser) { const orgPermissions = this.permissionsByCategory[AclCategory.ORG] ?? []; const { orgId: userOrgId } = user; @@ -279,70 +336,4 @@ export class ChannelPermission { subCategory === AclSubCategory.MEMBER && this.isAuthorizedToModerate(role) ); } - - /** - * canCreateChannelHelpers - */ - private userCanAddAnonymousToAcl(user: IDiscussionsUser) { - if (!this.permissionsByCategory[AclCategory.ANONYMOUS_USER]) { - return true; - } - return isOrgAdmin(user); - } - - private userCanAddUnauthenticatedToAcl(user: IDiscussionsUser) { - if (!this.permissionsByCategory[AclCategory.AUTHENTICATED_USER]) { - return true; - } - return isOrgAdmin(user); - } - - private userCanAddAllGroupsToAcl(user: IDiscussionsUser) { - const groupPermissions = this.permissionsByCategory[AclCategory.GROUP]; - const userGroupsById = this.mapUserGroupsById(user.groups); - - if (!groupPermissions) { - return true; - } - - return groupPermissions.every((permission) => { - const userGroup = userGroupsById[permission.key]; - - return ( - userGroup && - this.isMemberTypeAuthorized(userGroup) && - this.isGroupDiscussable(userGroup) - ); - }); - } - - private userCanAddAllOrgsToAcl(user: IDiscussionsUser) { - const orgPermissions = this.permissionsByCategory[AclCategory.ORG]; - - if (!orgPermissions) { - return true; - } - - return ( - isOrgAdmin(user) && - this.isEveryPermissionForUserOrg(user.orgId, orgPermissions) - ); - } - - private isEveryPermissionForUserOrg( - userOrgId: string, - orgPermissions: IChannelAclPermissionDefinition[] - ) { - return orgPermissions.every((permission) => { - const { key: orgId } = permission; - return userOrgId === orgId; - }); - } - - // for now user permissions are disabled on channel create - // since users are not notified and cannot opt out - private userCanAddUsersToAcl(user: IDiscussionsUser) { - const userPermissions = this.permissionsByCategory[AclCategory.USER]; - return !userPermissions; - } } diff --git a/packages/discussions/src/utils/channels/can-create-channel.ts b/packages/discussions/src/utils/channels/can-create-channel.ts index 4ec52a64ff5..9c6c0bd20f7 100644 --- a/packages/discussions/src/utils/channels/can-create-channel.ts +++ b/packages/discussions/src/utils/channels/can-create-channel.ts @@ -16,10 +16,10 @@ export function canCreateChannel( channel: IChannel, user: IUser | IDiscussionsUser = {} ): boolean { - const { channelAcl, access, groups, orgs } = channel; + const { channelAcl, access, groups, orgs, creator } = channel; if (channelAcl) { - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, creator); return channelPermission.canCreateChannel(user as IDiscussionsUser); } diff --git a/packages/discussions/src/utils/channels/can-modify-channel.ts b/packages/discussions/src/utils/channels/can-modify-channel.ts index f209c9c7652..0fe3754a010 100644 --- a/packages/discussions/src/utils/channels/can-modify-channel.ts +++ b/packages/discussions/src/utils/channels/can-modify-channel.ts @@ -15,14 +15,11 @@ export function canModifyChannel( channel: IChannel, user: IUser | IDiscussionsUser = {} ): boolean { - const { channelAcl } = channel; + const { channelAcl, creator } = channel; if (channelAcl) { - const channelPermission = new ChannelPermission(channelAcl); - return channelPermission.canModifyChannel( - user as IDiscussionsUser, - channel.creator - ); + const channelPermission = new ChannelPermission(channelAcl, creator); + return channelPermission.canModerateChannel(user as IDiscussionsUser); } return isAuthorizedToModifyChannelByLegacyPermissions(user, channel); diff --git a/packages/discussions/src/utils/channels/can-post-to-channel.ts b/packages/discussions/src/utils/channels/can-post-to-channel.ts index 8e2facd19a0..fa6786f8e62 100644 --- a/packages/discussions/src/utils/channels/can-post-to-channel.ts +++ b/packages/discussions/src/utils/channels/can-post-to-channel.ts @@ -28,10 +28,10 @@ export function canPostToChannel( channel: IChannel, user: IUser | IDiscussionsUser = {} ): boolean { - const { channelAcl, access, groups, orgs, allowAnonymous } = channel; + const { channelAcl, access, groups, orgs, allowAnonymous, creator } = channel; if (channelAcl) { - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, creator); return channelPermission.canPostToChannel(user as IDiscussionsUser); } diff --git a/packages/discussions/src/utils/posts/can-delete-post.ts b/packages/discussions/src/utils/posts/can-delete-post.ts new file mode 100644 index 00000000000..ad9f770eef3 --- /dev/null +++ b/packages/discussions/src/utils/posts/can-delete-post.ts @@ -0,0 +1,35 @@ +import { IUser } from "@esri/arcgis-rest-types"; +import { IChannel, IDiscussionsUser, IPost } from "../../types"; +import { ChannelPermission } from "../channel-permission"; + +/** + * Utility to determine if User has privileges to delete a post + * @param post + * @param user + * @param channel + * @returns {boolean} + */ +export function canDeletePost( + post: IPost, + channel: IChannel, + user: IUser | IDiscussionsUser = {} +): boolean { + return isPostCreator(post, user) || isChannelModerator(channel, user); +} + +function isPostCreator(post: IPost, user: IUser | IDiscussionsUser) { + return !!user.username && post.creator === user.username; +} + +function isChannelModerator( + channel: IChannel, + user: IUser | IDiscussionsUser +): boolean { + const { channelAcl, creator } = channel; + if (!channelAcl) { + return false; + } + + const channelPermission = new ChannelPermission(channelAcl, creator); + return channelPermission.canModerateChannel(user); +} diff --git a/packages/discussions/src/utils/posts/can-modify-post-status.ts b/packages/discussions/src/utils/posts/can-modify-post-status.ts index 1481eee6fb0..d57f1189984 100644 --- a/packages/discussions/src/utils/posts/can-modify-post-status.ts +++ b/packages/discussions/src/utils/posts/can-modify-post-status.ts @@ -15,14 +15,11 @@ export function canModifyPostStatus( channel: IChannel, user: IUser | IDiscussionsUser = {} ): boolean { - const { channelAcl } = channel; + const { channelAcl, creator } = channel; if (channelAcl) { - const channelPermission = new ChannelPermission(channelAcl); - return channelPermission.canModifyPostStatus( - user as IDiscussionsUser, - channel.creator - ); + const channelPermission = new ChannelPermission(channelAcl, creator); + return channelPermission.canModerateChannel(user as IDiscussionsUser); } return isAuthorizedToModifyStatusByLegacyPermissions(user, channel); diff --git a/packages/discussions/src/utils/posts/can-modify-post.ts b/packages/discussions/src/utils/posts/can-modify-post.ts index da5eda3cbda..a9348c2feeb 100644 --- a/packages/discussions/src/utils/posts/can-modify-post.ts +++ b/packages/discussions/src/utils/posts/can-modify-post.ts @@ -1,6 +1,7 @@ import { IGroup, IUser } from "@esri/arcgis-rest-types"; import { IChannel, IDiscussionsUser, IPost, SharingAccess } from "../../types"; import { CANNOT_DISCUSS } from "../constants"; +import { ChannelPermission } from "../channel-permission"; type ILegacyChannelPermissions = Pick< IChannel, @@ -19,7 +20,13 @@ export function canModifyPost( user: IUser | IDiscussionsUser = {}, channel: IChannel ): boolean { - const { access, groups, orgs, allowAnonymous } = channel; + const { access, groups, orgs, allowAnonymous, channelAcl, creator } = channel; + if (channelAcl) { + const channelPermission = new ChannelPermission(channelAcl, creator); + return ( + isPostCreator(post, user) && channelPermission.canPostToChannel(user) + ); + } return ( isPostCreator(post, user) && diff --git a/packages/discussions/src/utils/posts/index.ts b/packages/discussions/src/utils/posts/index.ts index 5fef6d675e3..92bb67e68c7 100644 --- a/packages/discussions/src/utils/posts/index.ts +++ b/packages/discussions/src/utils/posts/index.ts @@ -1,15 +1,9 @@ import { parseDatasetId } from "@esri/hub-common"; -import { - IChannel, - IDiscussionParams, - IDiscussionsUser, - IPost, -} from "../../types"; -import { IUser } from "@esri/arcgis-rest-auth"; -import { canModifyChannel } from "../channels"; +import { IDiscussionParams } from "../../types"; import { MENTION_ATTRIBUTE } from "../constants"; export { canModifyPost } from "./can-modify-post"; +export { canDeletePost } from "./can-delete-post"; export { canModifyPostStatus } from "./can-modify-post-status"; export { isDiscussable } from "@esri/hub-common"; @@ -51,25 +45,6 @@ export function parseDiscussionURI(discussion: string): IDiscussionParams { }; } -/** - * Determines if the given user has sufficient privileges to delete the given post - * @param post An IPost object - * @param channel An IChannel object - * @param user An IUser object - * @returns true if the user can delete the post - */ -export function canDeletePost( - post: IPost, - channel: IChannel, - user: IUser | IDiscussionsUser = {} -): boolean { - return isPostCreator(post, user) || canModifyChannel(channel, user); -} - -function isPostCreator(post: IPost, user: IUser | IDiscussionsUser) { - return !!user.username && post.creator === user.username; -} - const MENTION_ATTRIBUTE_AND_VALUE_PATTERN = new RegExp( `${MENTION_ATTRIBUTE}=('|")[\\w@\\.-]+('|")`, "g" diff --git a/packages/discussions/test/utils/channel-permission.test.ts b/packages/discussions/test/utils/channel-permission.test.ts index 473724c90d7..21b6291e4d5 100644 --- a/packages/discussions/test/utils/channel-permission.test.ts +++ b/packages/discussions/test/utils/channel-permission.test.ts @@ -89,7 +89,7 @@ describe("ChannelPermission class", () => { const user = buildUser(); const channelAcl = [] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -98,7 +98,7 @@ describe("ChannelPermission class", () => { const user = buildUser({ username: null }); const channelAcl = [] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -113,7 +113,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.ANONYMOUS_USER, role: allowedRole }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -125,7 +125,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.ANONYMOUS_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -140,7 +140,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.AUTHENTICATED_USER, role: allowedRole }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -152,7 +152,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.AUTHENTICATED_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -163,7 +163,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.AUTHENTICATED_USER, role: Role.READWRITE }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -193,7 +193,7 @@ describe("ChannelPermission class", () => { groups: [buildGroup(groupId1, memberType)], // member in groupId1 }); - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -217,7 +217,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -241,7 +241,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -269,7 +269,7 @@ describe("ChannelPermission class", () => { groups: [buildGroup(groupId1, memberType)], // admin in groupId1 }); - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -293,7 +293,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -333,7 +333,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -360,7 +360,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -387,7 +387,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -413,7 +413,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "joker"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -438,7 +438,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -461,7 +461,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); @@ -480,7 +480,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(true); }); @@ -492,66 +492,13 @@ describe("ChannelPermission class", () => { { category: AclCategory.USER, key: user.username, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canPostToChannel(user)).toBe(false); }); }); }); - describe("canModifyPostStatus", () => { - let canModifyChannelSpy: jasmine.Spy; - - beforeAll(() => { - canModifyChannelSpy = spyOn( - ChannelPermission.prototype, - "canModifyChannel" - ); - }); - - beforeEach(() => { - canModifyChannelSpy.calls.reset(); - }); - - it("should return true if canModifyChannel returns true", () => { - canModifyChannelSpy.and.callFake(() => true); - - const user = buildUser(); - const channelCreator = user.username; - const channelAcl = [] as IChannelAclPermission[]; - - const channelPermission = new ChannelPermission(channelAcl); - - expect(channelPermission.canModifyPostStatus(user, channelCreator)).toBe( - true - ); - - expect(canModifyChannelSpy.calls.count()).toBe(1); - const [arg1, arg2] = canModifyChannelSpy.calls.allArgs()[0]; // args for 1st call - expect(arg1).toBe(user); - expect(arg2).toBe(channelCreator); - }); - - it("should return false if canModifyChannel returns false", () => { - canModifyChannelSpy.and.callFake(() => false); - - const user = buildUser(); - const channelCreator = user.username; - const channelAcl = [] as IChannelAclPermission[]; - - const channelPermission = new ChannelPermission(channelAcl); - - expect(channelPermission.canModifyPostStatus(user, channelCreator)).toBe( - false - ); - - expect(canModifyChannelSpy.calls.count()).toBe(1); - const [arg1, arg2] = canModifyChannelSpy.calls.allArgs()[0]; // args for 1st call - expect(arg1).toBe(user); - expect(arg2).toBe(channelCreator); - }); - }); - describe("canCreateChannel", () => { describe("all permissions cases", () => { it("returns false if user not authenticated", () => { @@ -560,7 +507,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.ANONYMOUS_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -569,7 +516,7 @@ describe("ChannelPermission class", () => { const user = buildUser(); const channelAcl = [] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -578,7 +525,7 @@ describe("ChannelPermission class", () => { const user = buildUser({ role: "org_admin" }); const channelAcl = buildCompleteAcl() as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(true); }); @@ -591,7 +538,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.ANONYMOUS_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(true); }); @@ -602,7 +549,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.ANONYMOUS_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -615,7 +562,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.AUTHENTICATED_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(true); }); @@ -626,7 +573,7 @@ describe("ChannelPermission class", () => { { category: AclCategory.AUTHENTICATED_USER, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -662,7 +609,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(true); }); @@ -686,7 +633,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -713,7 +660,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -741,7 +688,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -765,7 +712,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(true); }); @@ -787,7 +734,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -809,7 +756,7 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); @@ -825,25 +772,23 @@ describe("ChannelPermission class", () => { role: Role.READ, }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); expect(channelPermission.canCreateChannel(user)).toEqual(false); }); }); }); - describe("canModifyChannel", () => { + describe("canModerateChannel", () => { describe("all permission cases", () => { it("returns false if user not logged in", async () => { const user = buildUser({ username: null }); const channelCreator = user.username; const channelAcl = [] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); it("returns true if the user created the channel", async () => { @@ -851,11 +796,12 @@ describe("ChannelPermission class", () => { const channelCreator = user.username; const channelAcl = [] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); - - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - true + const channelPermission = new ChannelPermission( + channelAcl, + channelCreator as string ); + + expect(channelPermission.canModerateChannel(user)).toBe(true); }); }); @@ -885,11 +831,9 @@ describe("ChannelPermission class", () => { groups: [buildGroup(groupId1, memberType)], // member in groupId1 }); - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect( - channelPermission.canModifyChannel(user, channelCreator) - ).toBe(true); + expect(channelPermission.canModerateChannel(user)).toBe(true); }); }); }); @@ -912,11 +856,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); it("returns false if user is group member in group permission list, role is allowed, but userMemberType is none", async () => { @@ -939,11 +881,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); it("returns true if user is group owner/admin in group permission list and role is allowed", async () => { @@ -971,11 +911,9 @@ describe("ChannelPermission class", () => { groups: [buildGroup(groupId1, memberType)], // admin or owner in groupId1 }); - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect( - channelPermission.canModifyChannel(user, channelCreator) - ).toBe(true); + expect(channelPermission.canModerateChannel(user)).toBe(true); }); }); }); @@ -998,11 +936,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); it("returns false if user is group admin but group is not in permissions list", async () => { @@ -1028,11 +964,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); }); @@ -1057,11 +991,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - true - ); + expect(channelPermission.canModerateChannel(user)).toBe(true); }); }); @@ -1085,11 +1017,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - true - ); + expect(channelPermission.canModerateChannel(user)).toBe(true); }); }); @@ -1111,11 +1041,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); }); @@ -1133,11 +1061,9 @@ describe("ChannelPermission class", () => { }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - true - ); + expect(channelPermission.canModerateChannel(user)).toBe(true); }); }); @@ -1148,11 +1074,9 @@ describe("ChannelPermission class", () => { { category: AclCategory.USER, key: user.username, role: Role.READ }, ] as IChannelAclPermission[]; - const channelPermission = new ChannelPermission(channelAcl); + const channelPermission = new ChannelPermission(channelAcl, "foo"); - expect(channelPermission.canModifyChannel(user, channelCreator)).toBe( - false - ); + expect(channelPermission.canModerateChannel(user)).toBe(false); }); }); }); diff --git a/packages/discussions/test/utils/channels/can-modify-channel.test.ts b/packages/discussions/test/utils/channels/can-modify-channel.test.ts index 46f20018189..7806ec68bcd 100644 --- a/packages/discussions/test/utils/channels/can-modify-channel.test.ts +++ b/packages/discussions/test/utils/channels/can-modify-channel.test.ts @@ -9,40 +9,39 @@ import { import { canModifyChannel } from "../../../src/utils/channels"; import { ChannelPermission } from "../../../src/utils/channel-permission"; -describe("canModifyChannel", () => { +describe("canModerateChannel", () => { describe("With channelAcl Permissions", () => { - let canModifyChannelSpy: jasmine.Spy; + let canModerateChannelSpy: jasmine.Spy; beforeAll(() => { - canModifyChannelSpy = spyOn( + canModerateChannelSpy = spyOn( ChannelPermission.prototype, - "canModifyChannel" + "canModerateChannel" ); }); beforeEach(() => { - canModifyChannelSpy.calls.reset(); + canModerateChannelSpy.calls.reset(); }); - it("return true if channelPermission.canModifyChannel is true", () => { - canModifyChannelSpy.and.callFake(() => true); + it("return true if channelPermission.canModerateChannel is true", () => { + canModerateChannelSpy.and.callFake(() => true); const user = {} as IDiscussionsUser; const channel = { - channelAcl: [{ category: AclCategory.ANONYMOUS_USER, role: Role.READ }], + channelAcl: [{ category: AclCategory.GROUP, role: Role.MANAGE }], creator: "john", } as IChannel; expect(canModifyChannel(channel, user)).toBe(true); - expect(canModifyChannelSpy.calls.count()).toBe(1); - const [arg1, arg2] = canModifyChannelSpy.calls.allArgs()[0]; // args for 1st call + expect(canModerateChannelSpy.calls.count()).toBe(1); + const [arg1] = canModerateChannelSpy.calls.allArgs()[0]; // args for 1st call expect(arg1).toBe(user); - expect(arg2).toBe(channel.creator); }); - it("return false if channelPermission.canModifyChannel is false", () => { - canModifyChannelSpy.and.callFake(() => false); + it("return false if channelPermission.canModerateChannel is false", () => { + canModerateChannelSpy.and.callFake(() => false); const user = {} as IDiscussionsUser; const channel = { @@ -52,10 +51,9 @@ describe("canModifyChannel", () => { expect(canModifyChannel(channel, user)).toBe(false); - expect(canModifyChannelSpy.calls.count()).toBe(1); - const [arg1, arg2] = canModifyChannelSpy.calls.allArgs()[0]; // args for 1st call + expect(canModerateChannelSpy.calls.count()).toBe(1); + const [arg1] = canModerateChannelSpy.calls.allArgs()[0]; // args for 1st call expect(arg1).toBe(user); - expect(arg2).toBe(channel.creator); }); }); diff --git a/packages/discussions/test/utils/posts/can-delete-post.test.ts b/packages/discussions/test/utils/posts/can-delete-post.test.ts new file mode 100644 index 00000000000..506566a79ea --- /dev/null +++ b/packages/discussions/test/utils/posts/can-delete-post.test.ts @@ -0,0 +1,80 @@ +import { IUser } from "@esri/arcgis-rest-types"; +import { + AclCategory, + IChannel, + IDiscussionsUser, + IPost, + Role, +} from "../../../src/types"; +import { canDeletePost } from "../../../src/utils/posts/can-delete-post"; +import { ChannelPermission } from "../../../src/utils/channel-permission"; + +describe("canDeletePost", () => { + it("returns true when the user created the post", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canModerateChannel" + ); + const post = { id: "post1", creator: "user1" } as IPost; + const user = { username: "user1" } as IUser; + const channel = { id: "channel1" } as IChannel; + const result = canDeletePost(post, channel, user); + expect(result).toBe(true); + expect(canModerateChannelSpy).not.toHaveBeenCalled(); + }); + + it("returns true when user did not create the post but user can moderate channel", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canModerateChannel" + ).and.returnValue(true); + const post = { id: "post1", creator: "user1" } as IPost; + const user = { username: "user2" } as IUser; + const channel = { + id: "channel1", + channelAcl: [{ category: AclCategory.ANONYMOUS_USER, role: Role.READ }], + } as IChannel; + const result = canDeletePost(post, channel, user); + expect(result).toBe(true); + expect(canModerateChannelSpy).toHaveBeenCalledTimes(1); + expect(canModerateChannelSpy).toHaveBeenCalledWith(user); + }); + + it("returns false when user did not create the post", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canModerateChannel" + ).and.returnValue(false); + const post = { id: "post1", creator: "user1" } as IPost; + const user = { username: "user2" } as IUser; + const channel = { id: "channel1" } as IChannel; + const result = canDeletePost(post, channel, user); + expect(result).toBe(false); + expect(canModerateChannelSpy).toHaveBeenCalledTimes(0); + }); + + it("returns false when the user is unauthenticated", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canModerateChannel" + ).and.returnValue(false); + const post = { id: "post1", creator: "user1" } as IPost; + const user = { username: null } as IDiscussionsUser; + const channel = { id: "channel1" } as IChannel; + const result = canDeletePost(post, channel, user); + expect(result).toBe(false); + expect(canModerateChannelSpy).toHaveBeenCalledTimes(0); + }); + + it("returns false when the user is undefined", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canModerateChannel" + ).and.returnValue(false); + const post = { id: "post1", creator: "user1" } as IPost; + const channel = { id: "channel1" } as IChannel; + const result = canDeletePost(post, channel); + expect(result).toBe(false); + expect(canModerateChannelSpy).toHaveBeenCalledTimes(0); + }); +}); diff --git a/packages/discussions/test/utils/posts/can-modify-post-status.test.ts b/packages/discussions/test/utils/posts/can-modify-post-status.test.ts index bebcef31cb9..ce8c877acd4 100644 --- a/packages/discussions/test/utils/posts/can-modify-post-status.test.ts +++ b/packages/discussions/test/utils/posts/can-modify-post-status.test.ts @@ -11,21 +11,21 @@ import { ChannelPermission } from "../../../src/utils/channel-permission"; describe("canModifyPostStatus", () => { describe("With channelAcl Permissions", () => { - let canModifyPostStatusSpy: jasmine.Spy; + let canModerateChannelSpy: jasmine.Spy; beforeAll(() => { - canModifyPostStatusSpy = spyOn( + canModerateChannelSpy = spyOn( ChannelPermission.prototype, - "canModifyPostStatus" + "canModerateChannel" ); }); beforeEach(() => { - canModifyPostStatusSpy.calls.reset(); + canModerateChannelSpy.calls.reset(); }); it("return true if channelPermission.canModifyPostStatus is true", () => { - canModifyPostStatusSpy.and.callFake(() => true); + canModerateChannelSpy.and.callFake(() => true); const user = {} as IDiscussionsUser; const channel = { @@ -35,14 +35,13 @@ describe("canModifyPostStatus", () => { expect(canModifyPostStatus(channel, user)).toBe(true); - expect(canModifyPostStatusSpy.calls.count()).toBe(1); - const [arg1, arg2] = canModifyPostStatusSpy.calls.allArgs()[0]; // args for 1st call + expect(canModerateChannelSpy.calls.count()).toBe(1); + const [arg1] = canModerateChannelSpy.calls.allArgs()[0]; // args for 1st call expect(arg1).toBe(user); - expect(arg2).toBe(channel.creator); }); it("return false if channelPermission.canModifyPostStatus is false", () => { - canModifyPostStatusSpy.and.callFake(() => false); + canModerateChannelSpy.and.callFake(() => false); const user = {} as IDiscussionsUser; const channel = { @@ -52,10 +51,9 @@ describe("canModifyPostStatus", () => { expect(canModifyPostStatus(channel, user)).toBe(false); - expect(canModifyPostStatusSpy.calls.count()).toBe(1); - const [arg1, arg2] = canModifyPostStatusSpy.calls.allArgs()[0]; // args for 1st call + expect(canModerateChannelSpy.calls.count()).toBe(1); + const [arg1] = canModerateChannelSpy.calls.allArgs()[0]; // args for 1st call expect(arg1).toBe(user); - expect(arg2).toBe(channel.creator); }); }); diff --git a/packages/discussions/test/utils/posts/can-modify-post.test.ts b/packages/discussions/test/utils/posts/can-modify-post.test.ts index c1c4fa7c0f0..368b7fd095d 100644 --- a/packages/discussions/test/utils/posts/can-modify-post.test.ts +++ b/packages/discussions/test/utils/posts/can-modify-post.test.ts @@ -7,6 +7,7 @@ import { } from "../../../src/types"; import { CANNOT_DISCUSS } from "../../../src/utils/constants"; import { canModifyPost } from "../../../src/utils/posts"; +import { ChannelPermission } from "../../../src/utils/channel-permission"; describe("canModifyPost", () => { it("returns false if the user did not create the post", () => { @@ -36,6 +37,32 @@ describe("canModifyPost", () => { expect(result).toBe(false); }); + it("returns false if the user created the post but can longer write to channel", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canPostToChannel" + ).and.returnValue(false); + const post = { id: "postId", creator: "john" } as IPost; + const user = { username: "john" } as IDiscussionsUser; + const channel = { channelAcl: [] } as unknown as IChannel; + + const result = canModifyPost(post, user, channel); + expect(result).toBe(false); + }); + + it("returns true if the user created the post and can still write to channel", () => { + const canModerateChannelSpy = spyOn( + ChannelPermission.prototype, + "canPostToChannel" + ).and.returnValue(true); + const post = { id: "postId", creator: "john" } as IPost; + const user = { username: "john" } as IDiscussionsUser; + const channel = { channelAcl: [] } as unknown as IChannel; + + const result = canModifyPost(post, user, channel); + expect(result).toBe(true); + }); + describe("Legacy Permissions", () => { describe("public channel", () => { it("returns true if user is creator", () => { diff --git a/packages/discussions/test/utils/posts/index.test.ts b/packages/discussions/test/utils/posts/index.test.ts index f68a05b9d9e..9bd4262fdd2 100644 --- a/packages/discussions/test/utils/posts/index.test.ts +++ b/packages/discussions/test/utils/posts/index.test.ts @@ -1,14 +1,8 @@ import { IGroup, IUser } from "@esri/arcgis-rest-types"; -import { - IChannel, - IDiscussionParams, - IDiscussionsUser, - IPost, -} from "../../../src/types"; +import { IDiscussionParams } from "../../../src/types"; import { isDiscussable, parseDiscussionURI, - canDeletePost, parseMentionedUsers, } from "../../../src/utils/posts"; import { @@ -16,7 +10,6 @@ import { MENTION_ATTRIBUTE, } from "../../../src/utils/constants"; import * as viewGroup from "@esri/hub-common/test/mocks/groups/view-group.json"; -import * as canModifyChannelModule from "../../../src/utils/channels/can-modify-channel"; describe("parseDiscussionURI", () => { it("returns DiscussionParams for valid discussion uri", () => { @@ -94,76 +87,6 @@ describe("isDiscussable", () => { }); }); -describe("canDeletePost", () => { - it("returns true when the user created the post", () => { - const canModifyChannelSpy = spyOn( - canModifyChannelModule, - "canModifyChannel" - ); - const post = { id: "post1", creator: "user1" } as IPost; - const user = { username: "user1" } as IUser; - const channel = { id: "channel1" } as IChannel; - const result = canDeletePost(post, channel, user); - expect(result).toBe(true); - expect(canModifyChannelSpy).not.toHaveBeenCalled(); - }); - - it("returns true when user did not create the post and user can modify channel", () => { - const canModifyChannelSpy = spyOn( - canModifyChannelModule, - "canModifyChannel" - ).and.returnValue(true); - const post = { id: "post1", creator: "user1" } as IPost; - const user = { username: "user2" } as IUser; - const channel = { id: "channel1" } as IChannel; - const result = canDeletePost(post, channel, user); - expect(result).toBe(true); - expect(canModifyChannelSpy).toHaveBeenCalledTimes(1); - expect(canModifyChannelSpy).toHaveBeenCalledWith(channel, user); - }); - - it("returns false when user did not create the post and user cannot modify channel", () => { - const canModifyChannelSpy = spyOn( - canModifyChannelModule, - "canModifyChannel" - ).and.returnValue(false); - const post = { id: "post1", creator: "user1" } as IPost; - const user = { username: "user2" } as IUser; - const channel = { id: "channel1" } as IChannel; - const result = canDeletePost(post, channel, user); - expect(result).toBe(false); - expect(canModifyChannelSpy).toHaveBeenCalledTimes(1); - expect(canModifyChannelSpy).toHaveBeenCalledWith(channel, user); - }); - - it("returns false when the user is unauthenticated", () => { - const canModifyChannelSpy = spyOn( - canModifyChannelModule, - "canModifyChannel" - ).and.returnValue(false); - const post = { id: "post1", creator: "user1" } as IPost; - const user = { username: null } as IDiscussionsUser; - const channel = { id: "channel1" } as IChannel; - const result = canDeletePost(post, channel, user); - expect(result).toBe(false); - expect(canModifyChannelSpy).toHaveBeenCalledTimes(1); - expect(canModifyChannelSpy).toHaveBeenCalledWith(channel, user); - }); - - it("returns false when the user is undefined", () => { - const canModifyChannelSpy = spyOn( - canModifyChannelModule, - "canModifyChannel" - ).and.returnValue(false); - const post = { id: "post1", creator: "user1" } as IPost; - const channel = { id: "channel1" } as IChannel; - const result = canDeletePost(post, channel); - expect(result).toBe(false); - expect(canModifyChannelSpy).toHaveBeenCalledTimes(1); - expect(canModifyChannelSpy).toHaveBeenCalledWith(channel, {}); - }); -}); - describe("MENTION_ATTRIBUTE", () => { it("should be defined", () => { expect(MENTION_ATTRIBUTE).toBeDefined();