diff --git a/test/api/unit/models/task.test.js b/test/api/unit/models/task.test.js
index 5921a4e2c2..5da3341cbe 100644
--- a/test/api/unit/models/task.test.js
+++ b/test/api/unit/models/task.test.js
@@ -246,13 +246,23 @@ describe('Task Model', () => {
expect(foundTasks[0].text).to.eql(taskWithAlias.text);
});
- it('scopes alias lookup to user', async () => {
+ it('scopes alias lookup to user when querying aliases only', async () => {
await Tasks.Task.findMultipleByIdOrAlias([taskWithAlias.alias], user._id);
+ expect(Tasks.Task.find).to.be.calledOnce;
+ expect(Tasks.Task.find).to.be.calledWithMatch({
+ alias: { $in: [taskWithAlias.alias] },
+ userId: user._id,
+ });
+ });
+
+ it('scopes alias lookup to user when querying aliases and IDs', async () => {
+ await Tasks.Task.findMultipleByIdOrAlias([taskWithAlias.alias, secondTask._id], user._id);
+
expect(Tasks.Task.find).to.be.calledOnce;
expect(Tasks.Task.find).to.be.calledWithMatch({
$or: [
- { _id: { $in: [] } },
+ { _id: { $in: [secondTask._id] } },
{ alias: { $in: [taskWithAlias.alias] } },
],
userId: user._id,
@@ -270,10 +280,7 @@ describe('Task Model', () => {
expect(Tasks.Task.find).to.be.calledOnce;
expect(Tasks.Task.find).to.be.calledWithMatch({
- $or: [
- { _id: { $in: [] } },
- { alias: { $in: [taskWithAlias.alias] } },
- ],
+ alias: { $in: [taskWithAlias.alias] },
userId: user._id,
foo: 'bar',
});
diff --git a/website/client/src/app.vue b/website/client/src/app.vue
index 64603fd122..e9afbb2515 100644
--- a/website/client/src/app.vue
+++ b/website/client/src/app.vue
@@ -42,7 +42,6 @@
-
-
-
-
- You're previewing new Habitica Groups!
-
-
- Have a question?
-
-
-
-
-
-
-
-
diff --git a/website/client/src/components/snackbars/notifications.vue b/website/client/src/components/snackbars/notifications.vue
index 9f1705d220..30bd8d73e9 100644
--- a/website/client/src/components/snackbars/notifications.vue
+++ b/website/client/src/components/snackbars/notifications.vue
@@ -143,7 +143,7 @@ export default {
scrollPosToCheck += this.eventPromoBannerHeight ?? 0;
}
- return scrollPosToCheck + 48; // teams testing banner is always on, for now
+ return scrollPosToCheck;
},
visibleNotificationsWithoutErrors () {
return this.visibleNotifications.filter(n => n.type !== 'error');
diff --git a/website/client/src/components/tasks/taskSummary.vue b/website/client/src/components/tasks/taskSummary.vue
index a42928968e..109d343c73 100644
--- a/website/client/src/components/tasks/taskSummary.vue
+++ b/website/client/src/components/tasks/taskSummary.vue
@@ -213,7 +213,8 @@ export default {
user: 'user.data',
}),
assignedUsernames () {
- if (!this.task.group || !this.task.group.assignedUsers) return [];
+ if (!this.task.group || !this.task.group.assignedUsers
+ || !this.task.group.assignedUsersDetail) return [];
const usernames = [];
for (const user of this.task.group.assignedUsers) {
usernames.push(this.task.group.assignedUsersDetail[user].assignedUsername);
diff --git a/website/common/script/ops/scoreTask.js b/website/common/script/ops/scoreTask.js
index e85b4509e0..14c0f688d8 100644
--- a/website/common/script/ops/scoreTask.js
+++ b/website/common/script/ops/scoreTask.js
@@ -4,6 +4,7 @@ import reduce from 'lodash/reduce';
import moment from 'moment';
import max from 'lodash/max';
import {
+ BadRequest,
NotAuthorized,
} from '../libs/errors';
import i18n from '../i18n';
@@ -247,6 +248,12 @@ export default function scoreTask (options = {}, req = {}, analytics) {
// If they're trying to purchase a too-expensive reward, don't allow them to do that.
if (task.value > user.stats.gp && task.type === 'reward') throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
+ // Thanks to open group tasks, userId is not guaranteed. Don't allow scoring inaccessible tasks
+ if (task.userId && task.userId !== user._id) {
+ throw new BadRequest('Cannot score task belonging to another user.');
+ } else if (user.guilds.indexOf(task.group.id) === -1 && user.party._id !== task.group.id) {
+ throw new BadRequest('Cannot score task belonging to another user.');
+ }
if (task.type === 'habit') {
delta += _changeTaskValue(user, task, direction, times, cron);
diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js
index 020625de7e..8a665e2da2 100644
--- a/website/server/controllers/api-v3/tasks.js
+++ b/website/server/controllers/api-v3/tasks.js
@@ -816,7 +816,7 @@ api.moveTask = {
const group = await getGroupFromTaskAndUser(task, user);
const challenge = await getChallengeFromTask(task);
if (task.group.id && !task.userId) {
- if (!group || user.guilds.concat(user.party._id).indexOf(group._id) === -1) {
+ if (!group || (user.guilds.indexOf(group._id) === -1 && user.party._id !== group._id)) {
throw new NotFound(res.t('groupNotFound'));
}
if (task.group.assignedUsers.length !== 0
diff --git a/website/server/libs/tasks/index.js b/website/server/libs/tasks/index.js
index f29839dbf7..faf36cd077 100644
--- a/website/server/libs/tasks/index.js
+++ b/website/server/libs/tasks/index.js
@@ -67,9 +67,7 @@ async function createTasks (req, res, options = {}) {
newTask.challenge.id = challenge.id;
} else if (group) {
newTask.group.id = group._id;
- if (taskData.requiresApproval) {
- newTask.group.approval.required = true;
- }
+ newTask.tags = [group._id];
newTask.group.managerNotes = taskData.managerNotes || '';
} else {
newTask.userId = user._id;
diff --git a/website/server/models/task.js b/website/server/models/task.js
index e09040a877..f9601cd10c 100644
--- a/website/server/models/task.js
+++ b/website/server/models/task.js
@@ -205,7 +205,7 @@ TaskSchema.statics.findByIdOrAlias = async function findByIdOrAlias (
return task;
};
-TaskSchema.statics.findMultipleByIdOrAlias = async function findByIdOrAlias (
+TaskSchema.statics.findMultipleByIdOrAlias = async function findMultipleByIdOrAlias (
identifiers,
userId,
additionalQueries = {},
@@ -226,6 +226,7 @@ TaskSchema.statics.findMultipleByIdOrAlias = async function findByIdOrAlias (
});
if (ids.length > 0 && aliases.length > 0) {
+ query.userId = userId;
query.$or = [
{ _id: { $in: ids } },
{ alias: { $in: aliases } },
@@ -233,6 +234,7 @@ TaskSchema.statics.findMultipleByIdOrAlias = async function findByIdOrAlias (
} else if (ids.length > 0) {
query._id = { $in: ids };
} else if (aliases.length > 0) {
+ query.userId = userId;
query.alias = { $in: aliases };
} else {
throw new Error('No identifiers found.'); // Should be covered by the !identifiers check, but..