From c680fec3aabb8feb51f52e73a69e5112480f01f1 Mon Sep 17 00:00:00 2001
From: Emmanuel Ormancey <emmanuel.ormancey@cern.ch>
Date: Mon, 17 Jan 2022 15:47:05 +0100
Subject: [PATCH] [#118] Merge targeted users and groups input

---
 src/models/channel.ts                         |  8 +++-
 .../impl/notifications/send-notification.ts   | 40 +++++++++++++++----
 2 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/src/models/channel.ts b/src/models/channel.ts
index 7fa8561c..cee299ec 100644
--- a/src/models/channel.ts
+++ b/src/models/channel.ts
@@ -316,7 +316,7 @@ export class Channel extends ApiKeyObject {
 
   // Checks if user has authorization to send Notification to the channel
   // via Form/API
-  async canSendByForm(authorizationBag: AuthorizationBag) {
+  async canSendByForm(authorizationBag: AuthorizationBag, targetedNotification: boolean = false) {
     // No permission to send by Form set
     if (!this.submissionByForm) return false;
 
@@ -336,6 +336,12 @@ export class Channel extends ApiKeyObject {
     if (this.submissionByForm.includes(SubmissionByForm.administrators) && (await this.isAdmin(authorizationBag))) {
       return true;
     }
+
+    // Targeted notifications are not allowed for members
+    if (targetedNotification) {
+      return false;
+    }
+
     // If user is channel member
     if (this.submissionByForm.includes(SubmissionByForm.members) && (await this.isMember(authorizationBag.user))) {
       return true;
diff --git a/src/services/impl/notifications/send-notification.ts b/src/services/impl/notifications/send-notification.ts
index be756576..d1192545 100644
--- a/src/services/impl/notifications/send-notification.ts
+++ b/src/services/impl/notifications/send-notification.ts
@@ -31,7 +31,12 @@ export class SendNotification implements Command {
     if (!targetChannel) throw new NotFoundError('Channel does not exist');
 
     if (this.authorizationBag) {
-      if (!(await targetChannel.canSendByForm(this.authorizationBag)))
+      if (
+        !(await targetChannel.canSendByForm(
+          this.authorizationBag,
+          this.notification.targetUsers || this.notification.targetGroups || this.notification.targetData,
+        ))
+      )
         throw new ForbiddenError('Sending to Channel not Authorized !');
     } else {
       // Call from the /unauthenticated
@@ -41,13 +46,38 @@ export class SendNotification implements Command {
     this.validateFields();
 
     let targetUsers = [];
+    let targetGroups = [];
+
+    // Extract targetData values (strings comma separated)
+    // and fill extracted values in targetUsers and targetGroups
+    if (this.notification.targetData) {
+      console.debug('Processing targetData', this.notification.targetData);
+      if (!this.notification.targetUsers) this.notification.targetUsers = [];
+      if (!this.notification.targetGroups) this.notification.targetGroups = [];
+
+      this.notification.targetData.forEach(id => {
+        if (!id) return;
+        const identifier = id.toLowerCase();
+        // no @ mean it's a group name (or a mistake that we'll ignore)
+        if (!identifier.includes('@')) this.notification.targetGroups.push({ groupIdentifier: identifier });
+        // @domain is not @cern.ch then it's an external user
+        else if (!identifier.includes('@cern.ch')) this.notification.targetUsers.push({ email: identifier });
+        // email@cern.ch contains a - so it's a group
+        else if (identifier.includes('-'))
+          this.notification.targetGroups.push({ groupIdentifier: identifier.replace('@cern.ch', '') });
+        // And the rest is handled as user email
+        else this.notification.targetUsers.push({ email: identifier });
+      });
+    }
+
     if (this.notification.targetUsers) {
+      console.debug('Processing targetUsers', this.notification.targetUsers);
       this.notification.private = true;
       targetUsers = await this.getOrCreateTargetUsers(transactionManager, targetChannel);
     }
 
-    let targetGroups = [];
     if (this.notification.targetGroups) {
+      console.debug('Processing targetGroups', this.notification.targetGroups);
       this.notification.private = true;
       targetGroups = await this.getOrCreateTargetGroups(transactionManager, targetChannel);
     }
@@ -146,9 +176,6 @@ export class SendNotification implements Command {
   async getOrCreateTargetUsers(transactionManager: EntityManager, targetChannel: Channel): Promise<User[]> {
     if (!targetChannel.sendPrivate) throw new ForbiddenError('This Channel does not allow direct notifications');
 
-    if (!(await targetChannel.isAdmin(this.authorizationBag)))
-      throw new ForbiddenError('Sending direct notifications to Channel not Authorized');
-
     const usersToSubscribe = [];
     const targetUsers = [];
     await Promise.all(
@@ -186,9 +213,6 @@ export class SendNotification implements Command {
   async getOrCreateTargetGroups(transactionManager: EntityManager, channel: Channel): Promise<Group[]> {
     if (!channel.sendPrivate) throw new ForbiddenError('This Channel does not allow direct notifications');
 
-    if (!(await channel.isAdmin(this.authorizationBag)))
-      throw new ForbiddenError('Sending direct notifications to Channel not Authorized');
-
     const groupsToSubscribe = [];
     const targetGroups = [];
     await Promise.all(
-- 
GitLab