Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions common/area.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ export type TAreaStatus = {
* @property status_logs - A list of past statuses
* @property required_certifications - (optional) UUIDs of certs required to
* use/reserve the area
* @property authorized_roles - (optional) A list of UserRole UUIDs that are
* allowed to see this area. A user must have at least one of these
* to reserve the area. If null, area needs to rolls to be reserved.
* @property available_to - (optional) A list of UserRole UUIDs that are
* allowed to reserve this area. If null, area needs no roles to be reserved.
* If set as the empty list, only admin users will be able to reserve this area.
* @property reservable - (optional) Whether this area is allowed to be reserved.
* Only users with the required certifications and authorized roles can
Expand All @@ -57,7 +56,7 @@ export type TArea = {
equipment?: MachineUUID[];
images?: FileUUID[];
required_certifications?: TRequiredCertificate[];
authorized_roles?: UserRoleUUID[] | null;
available_to?: UserRoleUUID[] | null;
reservable?: boolean;
reserved?: boolean;
visible_to?: UserRoleUUID[] | null;
Expand Down
5 changes: 3 additions & 2 deletions common/certification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export enum CERTIFICATION_VISIBILITY {
* certification, such as a training manual or a Google form link.
* @property required_certifications - (optional) a list of required certificates
* that are prerequisites to getting this cert
* @property authorized_roles - (optional) a list of UserRole UUIDs that are
* @property visible_to - (optional) a list of UserRole UUIDs that are
* allowed to see this certification. If not present, any user may see this
* certification
*/
Expand All @@ -42,7 +42,8 @@ export type TCertification = {
seconds_valid_for?: number;
documents?: TDocument[];
required_certifications?: TRequiredCertificate[];
authorized_roles?: UserRoleUUID[];
visible_to?: UserRoleUUID[];
// TODO? available_to
};

/**
Expand Down
4 changes: 2 additions & 2 deletions common/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type TFile = {
* TAreaDocument - A document link for users to access
* @property name - The name of the document
* @property link - The link to the document
* @property authorized_roles - (optional) A list of UserRole UUIDs that are
* @property visible_to - (optional) A list of UserRole UUIDs that are
* allowed to see this file. A user must have at least one of these
* roles to see the given file.
* If null, this file is public.
Expand All @@ -43,5 +43,5 @@ export type TFile = {
export type TDocument = {
name: string;
link: string;
authorized_roles?: UserRoleUUID[] | null;
visible_to?: UserRoleUUID[] | null;
};
15 changes: 11 additions & 4 deletions common/inventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export enum ITEM_RELATIVE_QUANTITY {
export type ItemQuantity = ITEM_RELATIVE_QUANTITY | number;

/**
* TLocation - Location of an inventory item
* TInventoryItemLocation - Location of an inventory item
* @property room - The UUID of an area where the item is stored
* @property quantity - of item stored in this location
* @property container - (optional) subsection of room
Expand Down Expand Up @@ -98,16 +98,20 @@ export const ITEM_ACCESS_DESCRIPTORS: {
* @property available - (optional) The current available quantity
* after accounting for checkouts/reservations (only applicable to items
* with a number quantity, not relative quantity)
* @property locations - See {@link TLocation} documentation
* @property locations - See {@link TInventoryItemLocation} documentation
* @property reorder_url - (optional) url for reordering item
* @property serial_number - (optional) serial number of item
* @property kit_contents - (optional) if kit, lists all item UUIDs in this kit
* @property keywords - (optional) keywords associated with item
* @property required_certs - UUIDs of certs required to use item
* @property authorized_roles - (optional) A list of UserRole UUIDs that are
* @property available_to - (optional) A list of UserRole UUIDs that are
* allowed to use this item. A user must have at least one of
* these roles to checkout the given item. If null, any user may
* checkout this item.
* @property visible_to - (optional) A list of UserRole UUIDs that are allowed
* to see this item. If null, the item is publicly visible.
* @property kit_contents - The contents of the item, if it is a kit
* @property parent_kit - The UUID of the parent kit (if this item is in one)
*/
export type TInventoryItem = {
uuid: InventoryItemUUID;
Expand All @@ -123,5 +127,8 @@ export type TInventoryItem = {
serial_number?: string;
keywords?: string[];
required_certifications?: TRequiredCertificate[];
authorized_roles?: UserRoleUUID[] | null;
available_to?: UserRoleUUID[] | null;
visible_to?: UserRoleUUID[] | null;
kit_contents?: InventoryItemUUID[];
parent_kit?: InventoryItemUUID;
};
14 changes: 9 additions & 5 deletions common/machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,14 @@ export type TMachineInstanceStatusLog = {
* @property documents - (optional) A list of {@link TDocument | Document} objects
* about this machine (manuals, data sheets, how-to videos, etc.)
* @property required_certs - UUIDs of certs required to use/reserve the machine
* @property authorized_roles - (optional) A list of UserRole UUIDs that are
* @property available_to - (optional) A list of UserRole UUIDs that are
* allowed to use this machine. A user must have at least one of these
* roles to see the given machine in the area tab and reserve the machine.
* If null, this machine is public and reservable by anyone.
* If set as an empty list, only admins are able to see or reserve the machine.
* roles to reserve the machine. If null, this machine is reservable by anyone;
* if an empty list, only admins are able to reserve the machine.
* @property visible_to - (optional) A list of UserRole UUIDs that are allowed to
* see this machine. A user must have at least one of these roles to see the
* given machine in the area tab. If null, this machine is public; if an empty
* list, this machine is only visible to admins.
* @property reservable - (optional) Whether this machine is allowed to be
* reserved. Only users with the required certifications and at least one
* authorized role can reserve this machine.
Expand All @@ -123,7 +126,8 @@ export type TMachine = {
status_logs: TMachineInstanceStatusLog[];
documents?: TDocument[];
required_certifications?: TRequiredCertificate[];
authorized_roles?: UserRoleUUID[] | null;
available_to?: UserRoleUUID[] | null;
visible_to?: UserRoleUUID[] | null;
reservable?: boolean;
reservation_type?:
| ITEM_ACCESS_TYPE.CHECKOUT_IN_SPACE
Expand Down
7 changes: 5 additions & 2 deletions common/workshop.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export type TWorkshopUserRecord = {
* signed in to the workshop
* @property images - (optional) a list UUIDs of {@link TFile | image Files} to
* display for this workshop
* @property authorized_roles - (optional) a list of UserRole UUIDs that are
* @property available_to - (optional) a list of UserRole UUIDs that are allowed
* to RSVP to this workshop. If null, any user may RSVP.
* @property visible_to - (optional) a list of UserRole UUIDs that are
* allowed to see this workshop. If null, any user may see this
* workshop
*/
Expand All @@ -56,7 +58,8 @@ export type TWorkshop = {
reminder_emails_sent: number[];
sign_in_list: TWorkshopUserRecord[];
images?: FileUUID[];
authorized_roles?: UserRoleUUID[] | null;
available_to?: UserRoleUUID[] | null;
visible_to?: UserRoleUUID[] | null;
};

/**
Expand Down
8 changes: 5 additions & 3 deletions server/controllers/area.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export async function setAllAreas(area_objs: TArea[]): Promise<TArea[] | null> {
equipment: area_obj.equipment,
images: area_obj.images,
required_certifications: area_obj.required_certifications,
authorized_roles: area_obj.authorized_roles,
available_to: area_obj.available_to,
reservable: area_obj.reservable,
reserved: area_obj.reserved,
visible_to: area_obj.visible_to,
Expand Down Expand Up @@ -193,7 +193,8 @@ export async function patchArea(
// refresh instance items
(partial_area.reservable !== undefined ||
partial_area.name ||
partial_area.authorized_roles ||
partial_area.available_to ||
partial_area.visible_to ||
partial_area.required_certifications)
) {
refreshAreaItem(updated_area);
Expand Down Expand Up @@ -278,7 +279,8 @@ export async function refreshAreaItem(area: TArea) {
},
],
required_certifications: area.required_certifications,
authorized_roles: area.authorized_roles,
available_to: area.available_to,
visible_to: area.visible_to,
};
new Inventory(instance_item_data).save();
} else if (area.reservable === false) {
Expand Down
6 changes: 3 additions & 3 deletions server/controllers/certification.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ export async function getCertificationsVisibleToUser(
{ visibility: CERTIFICATION_VISIBILITY.PUBLIC },
{
$or: [
{ authorized_roles: null },
{ authorized_roles: { $elemMatch: { $in: role_uuids } } },
{ visible_to: null },
{ visible_to: { $elemMatch: { $in: role_uuids } } },
],
},
],
Expand All @@ -97,7 +97,7 @@ async function getPublicCertifications(): Promise<TCertification[]> {
// Only return certifications that have require no roles
return Certifications.find({
visibility: { $ne: CERTIFICATION_VISIBILITY.SCHEDULE },
authorized_roles: null,
visible_to: null,
});
}

Expand Down
18 changes: 13 additions & 5 deletions server/controllers/checkout.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
} from "./machine.controller";
import { clearAreaReservations, patchArea } from "./area.controller";
import { getConfig } from "./config.controller";
import { mergeRequiredCerts } from "../../website/utils";

/**
* Get all checkouts in the database
Expand Down Expand Up @@ -109,18 +110,18 @@ export async function validateCheckout(
// For each item being checked out, verify that the user
// has authorized roles and required certs as needed
if (
item.authorized_roles !== null &&
item.authorized_roles !== undefined
item.available_to !== null &&
item.available_to !== undefined
) {
if (item.authorized_roles.length === 0) {
if (item.available_to.length === 0) {
// No user roles are authorized
return {
status: CHECKOUT_VALIDATION.MISSING_ROLE,
item_uuid: item.uuid,
};
}
if (
!item.authorized_roles.some((r) =>
!item.available_to.some((r) =>
user.active_roles.some((log) => log.role_uuid === r),
)
) {
Expand All @@ -132,7 +133,14 @@ export async function validateCheckout(
};
}
}
for (const cert of item.required_certifications || []) {

const parent_kit = await Inventory.findOne({
uuid: item.parent_kit
});

const mergedCerts = mergeRequiredCerts(item.required_certifications || [], parent_kit?.required_certifications || []);

for (const cert of mergedCerts) {
// User has no certs, so must not have the required certs.
if (!user.active_certificates) {
return {
Expand Down
5 changes: 3 additions & 2 deletions server/controllers/embed.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ export async function getEmbedsVisibleToUser(
$or: [
{
$not: {
$isArray: "$$docs.authorized_roles",
$isArray: "$$docs.visible_to",
},
},
{
$gt: [
{
$size: {
$setIntersection: [
"$$docs.authorized_roles",
"$$docs.visible_to",
role_uuids,
],
},
Expand All @@ -85,6 +85,7 @@ export async function getEmbedsVisibleToUser(
]);

// Consider filter out items that require certifications the user doesn't have
// Should potentially check parent kit's required certifications as well?
/*.filter((item) =>
item.required_certifications?.every((cert) =>
cert_uuids.includes(cert.certification_uuid),
Expand Down
20 changes: 16 additions & 4 deletions server/controllers/inventory.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export async function getInventoryVisibleToUser(
{
$match: {
$or: [
{ authorized_roles: null },
{ authorized_roles: { $in: role_uuids } },
{ visible_to: null },
{ visible_to: { $in: role_uuids } },
],
},
},
Expand All @@ -94,6 +94,7 @@ export async function getInventoryVisibleToUser(
]);

// Consider filter out items that require certifications the user doesn't have
// Should potentially check parent kit's required certifications as well?
/*.filter((item) =>
item.required_certifications?.every((cert) =>
cert_uuids.includes(cert.certification_uuid),
Expand All @@ -112,10 +113,10 @@ async function getPublicInventory(): Promise<TInventoryItem[]> {
const visible_areas = (await getPublicAreas()).map((area) => area.uuid);

// Find all items that need no roles or certifications
return await Inventory.aggregate([
let filteredInv = await Inventory.aggregate([
{
$match: {
authorized_roles: null,
visible_to: null,
// Required certifications must either be empty or not exist
$or: [
{ required_certifications: null },
Expand All @@ -137,6 +138,17 @@ async function getPublicInventory(): Promise<TInventoryItem[]> {
},
},
]);

// if an item is in the filtered list but its parent kit isn't, it shouldn't be
// visible (parent kit isn't publicly visible. ...or DNE ig)
for (let i = filteredInv.length - 1; i >= 0; i--) {
let item = filteredInv[i];
if (item.parent_kit && !filteredInv.some(itm => itm.uuid == item.parent_kit)) {
filteredInv.splice(i, 1);
}
}

return filteredInv;
}

/**
Expand Down
16 changes: 9 additions & 7 deletions server/controllers/machine.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export async function getMachinesVisibleToUser(
{
$match: {
$or: [
{ authorized_roles: null },
{ authorized_roles: { $in: role_uuids } },
{ visible_to: null },
{ visible_to: { $in: role_uuids } },
],
},
},
Expand All @@ -85,15 +85,15 @@ export async function getMachinesVisibleToUser(
$or: [
{
$not: {
$isArray: "$$docs.authorized_roles",
$isArray: "$$docs.visible_to",
},
},
{
$gt: [
{
$size: {
$setIntersection: [
"$$docs.authorized_roles",
"$$docs.visible_to",
role_uuids,
],
},
Expand All @@ -119,7 +119,7 @@ async function getPublicMachines(): Promise<TPublicMachineData[]> {
const Machines = mongoose.model("Machine", Machine, "machines");
// Get all machines that are public
return Machines.find({
authorized_roles: null,
visible_to: null,
}).select([
// Remove private information from the machine
"-status_logs",
Expand Down Expand Up @@ -220,7 +220,8 @@ export async function patchMachine(
// refresh instance items
(partial_machine.reservable !== undefined ||
partial_machine.name ||
partial_machine.authorized_roles ||
partial_machine.available_to ||
partial_machine.visible_to ||
partial_machine.reservation_type ||
partial_machine.required_certifications)
) {
Expand Down Expand Up @@ -312,7 +313,8 @@ export async function refreshMachineItems(
access_type: machine.reservation_type,
locations: locations,
required_certifications: machine.required_certifications,
authorized_roles: machine.authorized_roles,
available_to: machine.available_to,
visible_to: machine.visible_to,
};
new Inventory(instance_item_data).save();
});
Expand Down
Loading
Loading