Skip to content

Commit

Permalink
Add access level to user group memberships
Browse files Browse the repository at this point in the history
  • Loading branch information
rogup committed Aug 11, 2024
1 parent bf25adf commit f1c5dee
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 38 deletions.
21 changes: 21 additions & 0 deletions migrations/2024080611510000-add-user_group_memberships-access.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use strict";

module.exports = {
async up(queryInterface, Sequelize) {
// set access to ReadWrite for all current user_group_memberships
await queryInterface.sequelize.query(
`ALTER TABLE user_group_memberships
ADD access int(11) DEFAULT 3;`
);
await queryInterface.sequelize.query(
`ALTER TABLE user_group_memberships
MODIFY COLUMN access int(11) NOT NULL;`
);
},
async down(queryInterface, Sequelize) {
await queryInterface.sequelize.query(
`ALTER TABLE user_group_memberships
DROP access;`
);
},
};
12 changes: 11 additions & 1 deletion src/queries/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const UserModel = sequelize.define(

username: { type: DataTypes.STRING, allowNull: false },
password: { type: DataTypes.STRING, allowNull: false },
access: DataTypes.INTEGER, // 1 = member, 2 = admin
access: DataTypes.INTEGER,
enabled: DataTypes.INTEGER,
is_super_user: { type: DataTypes.BOOLEAN, allowNull: false },

Expand Down Expand Up @@ -275,6 +275,10 @@ const UserGroupMembershipModel = sequelize.define(
references: { model: UserGroupModel, key: "iduser_groups" },
allowNull: false,
},
access: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
tableName: "user_group_memberships",
Expand Down Expand Up @@ -420,6 +424,12 @@ export enum UserMapAccess {
Readwrite = 3,
}

/* The access values in the UserGroupMembership table */
export enum UserGroupAccess {
Readonly = 1,
Readwrite = 3,
}

/* All the possible values of the iditem_types column in the ItemType table */
export enum ItemTypeId {
Marker = 0,
Expand Down
97 changes: 69 additions & 28 deletions src/queries/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ describe("findAllDataGroupContentForUser", () => {
});

context(
"User is in 1 user group and there is 1 public user group, each associated with 1 data group",
"There is a user group (1) associated with 1 data group containing 1 marker, and a user group (2) associated with a data group containing 1 polygon",
() => {
const testMarker = {
idmarkers: 1,
Expand Down Expand Up @@ -194,46 +194,27 @@ describe("findAllDataGroupContentForUser", () => {

const testUserGroup1 = {
iduser_groups: 1,
name: "Test User Group 1 (Public)",
name: "User Group (1)",
};

const testDataGroup1 = {
iddata_groups: 1,
title: "Test Data Group 1 (Public)",
title: "Data Group (1)",
hex_colour: "#FF0001",
};

const testUserGroup2 = {
iduser_groups: 2,
name: "Test User Group 2",
name: "User Group (2)",
};

const testDataGroup2 = {
iddata_groups: 2,
title: "Test Data Group 2",
title: "Data Group (2)",
hex_colour: "#FF0002",
};

it("returns user groups and associated data groups with markers, polygons, and lines", async () => {
// Arrange

sandbox.replace(
Model.UserGroupMembership,
"findAll",
fake.resolves([
{
iduser_group_memberships: 1,
user_id: -1, // -1 means public
user_group_id: testUserGroup1.iduser_groups,
},
{
iduser_group_memberships: 2,
user_id: testUserId,
user_group_id: testUserGroup2.iduser_groups,
},
])
);

beforeEach(() => {
sandbox.replace(
Model.UserGroup,
"findOne",
Expand Down Expand Up @@ -295,17 +276,35 @@ describe("findAllDataGroupContentForUser", () => {
})
);
sandbox.replace(Model.Line, "findAll", fake.resolves([]));
});

// Act
it("Returns the datagroups for a usergroup with readwrite access and a public usergroup with readonly access", async () => {
sandbox.replace(
Model.UserGroupMembership,
"findAll",
fake.resolves([
{
iduser_group_memberships: 1,
user_id: -1, // -1 means public
user_group_id: testUserGroup1.iduser_groups,
access: 1, // readonly access
},
{
iduser_group_memberships: 2,
user_id: testUserId,
user_group_id: testUserGroup2.iduser_groups,
access: 3, // readwrite access
},
])
);

const result = await query.findAllDataGroupContentForUser(testUserId);

// Assert

const expectedContent = [
{
name: testUserGroup1.name,
id: testUserGroup1.iduser_groups,
access: 1,
dataGroups: [
{
...testDataGroup1,
Expand All @@ -318,6 +317,7 @@ describe("findAllDataGroupContentForUser", () => {
{
name: testUserGroup2.name,
id: testUserGroup2.iduser_groups,
access: 3,
dataGroups: [
{
...testDataGroup2,
Expand All @@ -331,6 +331,47 @@ describe("findAllDataGroupContentForUser", () => {

expect(result).to.deep.equal(expectedContent);
});

it("Returns the higher access level if a user has readwrite access to a public usergroup", async () => {
sandbox.replace(
Model.UserGroupMembership,
"findAll",
fake.resolves([
{
iduser_group_memberships: 1,
user_id: -1, // -1 means public
user_group_id: testUserGroup1.iduser_groups,
access: 1, // readonly access
},
{
iduser_group_memberships: 2,
user_id: testUserId,
user_group_id: testUserGroup1.iduser_groups,
access: 3, // readwrite access
},
])
);

const result = await query.findAllDataGroupContentForUser(testUserId);

const expectedContent = [
{
name: testUserGroup1.name,
id: testUserGroup1.iduser_groups,
access: 3,
dataGroups: [
{
...testDataGroup1,
markers: [testMarker],
polygons: [],
lines: [],
},
],
},
];

expect(result).to.deep.equal(expectedContent);
});
}
);
});
21 changes: 19 additions & 2 deletions src/queries/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,25 @@ export const findAllDataGroupContentForUser = async (userId: number) => {
where: {
iduser_groups: membership.user_group_id,
},
raw: true,
});
userGroup.access = membership.access;

// Check that user group actually exists
if (userGroup) {
userGroups.push(userGroup);
const existingUserGroup = userGroups.find(
(group) => group.iduser_groups === userGroup.iduser_groups
);

// If the user group is already in the list, use the highest access level
if (existingUserGroup) {
existingUserGroup.access = Math.max(
existingUserGroup.access,
userGroup.access
);
} else {
userGroups.push(userGroup);
}
}
}

Expand Down Expand Up @@ -332,20 +347,22 @@ export const findAllDataGroupContentForUser = async (userId: number) => {
userGroupsAndData.push({
name: group.name,
id: group.iduser_groups,
access: group.access,
dataGroups,
});
}

return userGroupsAndData;
};

export const hasAccessToDataGroup = async (
export const hasWriteAccessToDataGroup = async (
userId: number,
dataGroupId: number
): Promise<boolean> => {
const userGroupMemberships = await UserGroupMembership.findAll({
where: {
user_id: userId,
access: 3,
},
});

Expand Down
14 changes: 7 additions & 7 deletions src/routes/datagroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
updateLine,
} from "../queries/object";
import {
hasAccessToDataGroup,
hasWriteAccessToDataGroup,
findAllDataGroupContentForUser,
} from "../queries/query";
import { v4 as uuidv4 } from "uuid";
Expand Down Expand Up @@ -58,7 +58,7 @@ async function saveDataGroupMarker(
): Promise<ResponseObject> {
const { object, dataGroupId } = request.payload;

const hasAccess = await hasAccessToDataGroup(
const hasAccess = await hasWriteAccessToDataGroup(
request.auth.credentials.user_id,
dataGroupId
);
Expand All @@ -84,7 +84,7 @@ async function saveDataGroupPolygon(
): Promise<ResponseObject> {
const { object, dataGroupId } = request.payload;

const hasAccess = await hasAccessToDataGroup(
const hasAccess = await hasWriteAccessToDataGroup(
request.auth.credentials.user_id,
dataGroupId
);
Expand Down Expand Up @@ -113,7 +113,7 @@ async function saveDataGroupLine(
): Promise<ResponseObject> {
const { object, dataGroupId } = request.payload;

const hasAccess = await hasAccessToDataGroup(
const hasAccess = await hasWriteAccessToDataGroup(
request.auth.credentials.user_id,
dataGroupId
);
Expand Down Expand Up @@ -160,7 +160,7 @@ async function editDataGroupMarker(
): Promise<ResponseObject> {
const { uuid, name, description, dataGroupId } = request.payload;

const hasAccess = await hasAccessToDataGroup(
const hasAccess = await hasWriteAccessToDataGroup(
request.auth.credentials.user_id,
dataGroupId
);
Expand All @@ -179,7 +179,7 @@ async function editDataGroupPolygon(
): Promise<ResponseObject> {
const { uuid, name, description, dataGroupId } = request.payload;

const hasAccess = await hasAccessToDataGroup(
const hasAccess = await hasWriteAccessToDataGroup(
request.auth.credentials.user_id,
dataGroupId
);
Expand All @@ -198,7 +198,7 @@ async function editDataGroupLine(
): Promise<ResponseObject> {
const { uuid, name, description, dataGroupId } = request.payload;

const hasAccess = await hasAccessToDataGroup(
const hasAccess = await hasWriteAccessToDataGroup(
request.auth.credentials.user_id,
dataGroupId
);
Expand Down

0 comments on commit f1c5dee

Please sign in to comment.