Beispiel #1
0
def process_area_access_record_with_parents(user: User):
    show_not_qualified_areas = get_customization(
        'dashboard_display_not_qualified_areas')
    records = AreaAccessRecord.objects.filter(end=None, staff_charge=None)
    if not user.is_staff and show_not_qualified_areas != 'enabled':
        records = records.filter(area__in=user.accessible_areas())
    records = records.prefetch_related('customer', 'project', 'area')
    no_occupants = not records.exists()
    area_items = None
    area_model_tree = get_area_model_tree()
    if not no_occupants:
        areas_and_parents = area_model_tree.get_ancestor_areas(
            area_model_tree.get_areas([record.area.id for record in records]),
            include_self=True)
        # Sort to have area without children before others
        areas_and_parents.sort(key=lambda x: f'{x.tree_category}zz'
                               if x.is_leaf else f'{x.tree_category}/aa')
        area_summary = create_area_summary(area_model_tree=area_model_tree,
                                           add_resources=False,
                                           add_outages=False,
                                           add_occupants=True)
        area_summary_dict = {area['id']: area for area in area_summary}
        for area_item in areas_and_parents:
            area_item.item = area_summary_dict[area_item.id]
        area_items = area_tree_helper(areas_and_parents, records)
    return area_items, no_occupants
Beispiel #2
0
def check_policy_to_enter_this_area(area: Area, user: User):
	# If explicitly set on the Physical Access Level, staff may be exempt from being granted explicit access
	if user.is_staff and any([access_level.accessible() for access_level in PhysicalAccessLevel.objects.filter(allow_staff_access=True, area=area)]):
		pass
	else:
		# Check if the user normally has access to this area door at the current time (or access to any parent)
		if not any([access_level.accessible() for access_level in user.accessible_access_levels_for_area(area)]):
			first_access_exception = next(iter([access_level.ongoing_exception() for access_level in user.accessible_access_levels_for_area(area)]), None)
			raise NoAccessiblePhysicalAccessUserError(user=user, area=area, access_exception=first_access_exception)

	if not user.is_staff and not user.is_service_personnel:
		for a in area.get_ancestors(ascending=True, include_self=True):
			unavailable_resources = a.required_resources.filter(available=False)
			if unavailable_resources:
				raise UnavailableResourcesUserError(user=user, area=a, resources=unavailable_resources)

		# Non staff users may not enter an area during a scheduled outage.
		if area.scheduled_outage_in_progress():
			raise ScheduledOutageInProgressError(user=user, area=area)

		# If we reached maximum capacity, fail (only for non staff users)
		for a in area.get_ancestors(ascending=True, include_self=True):
			if a.maximum_capacity and 0 < a.maximum_capacity <= a.occupancy_count():
				raise MaximumCapacityReachedError(user=user, area=a)

		if area.requires_reservation and not area.get_current_reservation_for_user(user):
			raise ReservationRequiredUserError(user=user, area=area)
Beispiel #3
0
def check_policy_to_enter_any_area(user: User):
	"""
	Checks the area access policy for a user.
	"""
	if not user.is_active:
		raise InactiveUserError(user=user)

	if user.active_project_count() < 1:
		raise NoActiveProjectsForUserError(user=user)

	if user.access_expiration is not None and user.access_expiration < date.today():
		raise PhysicalAccessExpiredUserError(user=user)

	user_has_access_to_at_least_one_area = user.accessible_access_levels().exists()
	if not user_has_access_to_at_least_one_area:
		raise NoPhysicalAccessUserError(user=user)
Beispiel #4
0
def log_out_user(user: User):
	record = user.area_access_record()
	# Allow the user to log out of any area, even if this is a logout tablet for a different area.
	if record:
		record.end = timezone.now()
		record.save()
		# Shorten the user's area reservation since the user is now leaving
		shorten_reservation(user, record.area)
		# Stop charging area access if staff is leaving the area
		staff_charge = user.get_staff_charge()
		if staff_charge:
			try:
				staff_area_access = AreaAccessRecord.objects.get(staff_charge=staff_charge, end=None)
				staff_area_access.end = timezone.now()
				staff_area_access.save()
			except AreaAccessRecord.DoesNotExist:
				pass
Beispiel #5
0
def load_areas_for_use_in_template(user: User):
	"""
	This method returns accessible areas for the user and a queryset ready to be used in template view.
	The template view needs to use the {% recursetree %} tag from mptt
	"""
	accessible_areas = user.accessible_areas()
	areas = list(set([ancestor for area in accessible_areas for ancestor in area.get_ancestors(include_self=True)]))
	areas.sort(key=lambda x: x.tree_category())
	areas = Area.objects.filter(id__in=[area.id for area in areas])
	return accessible_areas, areas
Beispiel #6
0
def check_policy_to_enter_any_area(user: User):
	"""
	Checks the area access policy for a user.
	"""
	if not user.is_active:
		raise InactiveUserError(user=user)

	if user.active_project_count() < 1:
		raise NoActiveProjectsForUserError(user=user)

	if user.access_expiration is not None and user.access_expiration < date.today():
		raise PhysicalAccessExpiredUserError(user=user)

	user_has_access_to_at_least_one_area = user.physical_access_levels.all().exists()
	staff_has_access_to_at_least_one_area = user.is_staff and PhysicalAccessLevel.objects.filter(allow_staff_access=True).exists()
	if not (user_has_access_to_at_least_one_area or staff_has_access_to_at_least_one_area):
		raise NoPhysicalAccessUserError(user=user)
Beispiel #7
0
def check_user_reply_error(buddy_request: BuddyRequest,
                           user: User) -> Optional[str]:
    error_message = None
    try:
        check_policy_to_enter_any_area(user)
    except InactiveUserError:
        error_message = "You cannot reply to this request because your account has been deactivated"
    except NoActiveProjectsForUserError:
        error_message = "You cannot reply to this request because you don't have any active projects"
    except PhysicalAccessExpiredUserError:
        error_message = "You cannot reply to this request because your facility access has expired"
    except NoPhysicalAccessUserError:
        error_message = "You cannot reply to this request because you do not have access to any areas"
    else:
        if buddy_request.area not in user.accessible_areas():
            error_message = (
                f"You cannot reply to this request because you do not have access to the {buddy_request.area.name}"
            )
    return error_message
Beispiel #8
0
def check_billing_to_project(project: Project, user: User, item: Union[Tool, Area, Consumable, StaffCharge] = None):
	if project:
		if project not in user.active_projects():
			raise NotAllowedToChargeProjectException(project=project, user=user)

		if item:
			# Check if project only allows billing for certain tools
			allowed_tools = project.only_allow_tools.all()
			if allowed_tools.exists():
				if isinstance(item, Tool) and item not in allowed_tools:
					msg = f"{item.name} is not allowed for project {project.name}"
					raise ItemNotAllowedForProjectException(project, user, item.name, msg)
				elif isinstance(item, Area) and item.id not in distinct_qs_value_list(
						allowed_tools, '_requires_area_access_id'):
					msg = f"{item.name} is not allowed for project {project.name}"
					raise ItemNotAllowedForProjectException(project, user, item.name, msg)
			# Check if consumable withdrawals are allowed
			if isinstance(item, Consumable):
				if not project.allow_consumable_withdrawals:
					msg = f"Consumable withdrawals are not allowed for project {project.name}"
					raise ItemNotAllowedForProjectException(project, user, "Staff Charges", msg)
Beispiel #9
0
def check_policy_to_enable_tool(tool: Tool, operator: User, user: User,
                                project: Project, staff_charge: bool):
    """
	Check that the user is allowed to enable the tool. Enable the tool if the policy checks pass.
	"""
    facility_name = get_customization('facility_name')

    # The tool must be visible (or the parent if it's a child tool) to users.
    visible = tool.parent_tool.visible if tool.is_child_tool(
    ) else tool.visible
    if not visible:
        return HttpResponseBadRequest(
            "This tool is currently hidden from users.")

    # The tool must be operational.
    # If the tool is non-operational then it may only be accessed by staff members.
    if not tool.operational and not operator.is_staff:
        return HttpResponseBadRequest(
            "This tool is currently non-operational.")

    # The tool must not be in use.
    current_usage_event = tool.get_current_usage_event()
    if current_usage_event:
        return HttpResponseBadRequest("The tool is currently being used by " +
                                      str(current_usage_event.user) + ".")

    # The user must be qualified to use the tool itself, or the parent tool in case of alternate tool.
    tool_to_check_qualifications = tool.parent_tool if tool.is_child_tool(
    ) else tool
    if tool_to_check_qualifications not in operator.qualifications.all(
    ) and not operator.is_staff:
        return HttpResponseBadRequest(
            "You are not qualified to use this tool.")

    # Only staff members can operate a tool on behalf of another user.
    if (user and operator.pk != user.pk) and not operator.is_staff:
        return HttpResponseBadRequest(
            "You must be a staff member to use a tool on another user's behalf."
        )

    # All required resources must be available to operate a tool except for staff.
    if tool.required_resource_set.filter(
            available=False).exists() and not operator.is_staff:
        return HttpResponseBadRequest(
            "A resource that is required to operate this tool is unavailable.")

    # The tool operator may not activate tools in a particular area unless they are logged in to the area.
    # Staff are exempt from this rule.
    if tool.requires_area_access and AreaAccessRecord.objects.filter(
            area=tool.requires_area_access,
            customer=operator,
            staff_charge=None,
            end=None).count() == 0 and not operator.is_staff:
        dictionary = {'operator': operator, 'tool': tool, 'type': 'access'}
        abuse_email_address = get_customization('abuse_email_address')
        message = get_media_file_contents(
            'unauthorized_tool_access_email.html')
        if abuse_email_address and message:
            rendered_message = Template(message).render(Context(dictionary))
            send_mail("Area access requirement", rendered_message,
                      abuse_email_address, [abuse_email_address])
        return HttpResponseBadRequest(
            "You must be logged in to the {} to operate this tool.".format(
                tool.requires_area_access.name))

    # The tool operator may not activate tools in a particular area unless they are still within that area reservation window
    if not operator.is_staff and tool.requires_area_reservation():
        if not tool.requires_area_access.get_current_reservation_for_user(
                operator):
            dictionary = {
                'operator': operator,
                'tool': tool,
                'type': 'reservation',
            }
            abuse_email_address = get_customization('abuse_email_address')
            message = get_media_file_contents(
                'unauthorized_tool_access_email.html')
            if abuse_email_address and message:
                rendered_message = Template(message).render(
                    Context(dictionary))
                send_mail("Area reservation requirement", rendered_message,
                          abuse_email_address, [abuse_email_address])
            return HttpResponseBadRequest(
                "You must have a current reservation for the {} to operate this tool."
                .format(tool.requires_area_access.name))

    # Staff may only charge staff time for one user at a time.
    if staff_charge and operator.charging_staff_time():
        return HttpResponseBadRequest(
            'You are already charging staff time. You must end the current staff charge before you being another.'
        )

    # Staff may not bill staff time to the themselves.
    if staff_charge and operator == user:
        return HttpResponseBadRequest(
            'You cannot charge staff time to yourself.')

    # Users may only charge to projects they are members of.
    if project not in user.active_projects():
        return HttpResponseBadRequest(
            'The designated user is not assigned to the selected project.')

    # The tool operator must not have a lock on usage
    if operator.training_required:
        return HttpResponseBadRequest(
            f"You are blocked from using all tools in the {facility_name}. Please complete the {facility_name} rules tutorial in order to use tools."
        )

    # Users may only use a tool when delayed logoff is not in effect. Staff are exempt from this rule.
    if tool.delayed_logoff_in_progress() and not operator.is_staff:
        return HttpResponseBadRequest(
            "Delayed tool logoff is in effect. You must wait for the delayed logoff to expire before you can use the tool."
        )

    # Users may not enable a tool during a scheduled outage. Staff are exempt from this rule.
    if tool.scheduled_outage_in_progress() and not operator.is_staff:
        return HttpResponseBadRequest(
            "A scheduled outage is in effect. You must wait for the outage to end before you can use the tool."
        )

    return HttpResponse()
Beispiel #10
0
def check_policy_to_enable_tool(tool: Tool, operator: User, user: User, project: Project, staff_charge: bool):
	"""
	Check that the user is allowed to enable the tool. Enable the tool if the policy checks pass.
	"""
	facility_name = get_customization('facility_name')

	# The tool must be visible (or the parent if it's a child tool) to users.
	visible = tool.parent_tool.visible if tool.is_child_tool() else tool.visible
	if not visible:
		return HttpResponseBadRequest("This tool is currently hidden from users.")

	# The tool must be operational.
	# If the tool is non-operational then it may only be accessed by staff members or service personnel.
	if not tool.operational and not operator.is_staff and not operator.is_service_personnel:
		return HttpResponseBadRequest("This tool is currently non-operational.")

	# The tool must not be in use.
	current_usage_event = tool.get_current_usage_event()
	if current_usage_event:
		return HttpResponseBadRequest("The tool is currently being used by " + str(current_usage_event.user) + ".")

	# The user must be qualified to use the tool itself, or the parent tool in case of alternate tool.
	tool_to_check_qualifications = tool.parent_tool if tool.is_child_tool() else tool
	if tool_to_check_qualifications not in operator.qualifications.all() and not operator.is_staff:
		return HttpResponseBadRequest("You are not qualified to use this tool.")

	# Only staff members can operate a tool on behalf of another user.
	if (user and operator.pk != user.pk) and not operator.is_staff:
		return HttpResponseBadRequest("You must be a staff member to use a tool on another user's behalf.")

	# All required resources must be available to operate a tool except for staff or service personnel.
	if tool.required_resource_set.filter(available=False).exists() and not operator.is_staff and not operator.is_service_personnel:
		return HttpResponseBadRequest("A resource that is required to operate this tool is unavailable.")

	# The tool operator may not activate tools in a particular area unless they are logged in to the area.
	# Staff are exempt from this rule.
	if tool.requires_area_access and AreaAccessRecord.objects.filter(area=tool.requires_area_access, customer=operator, staff_charge=None, end=None).count() == 0 and not operator.is_staff:
		abuse_email_address = get_customization('abuse_email_address')
		message = get_media_file_contents('unauthorized_tool_access_email.html')
		if abuse_email_address and message:
			dictionary = {
				'operator': operator,
				'tool': tool,
				'type': 'access'
			}
			rendered_message = Template(message).render(Context(dictionary))
			send_mail(subject="Area access requirement", content=rendered_message, from_email=abuse_email_address, to=[abuse_email_address], email_category=EmailCategory.ABUSE)
		return HttpResponseBadRequest("You must be logged in to the {} to operate this tool.".format(tool.requires_area_access.name))

	# The tool operator may not activate tools in a particular area unless they are still within that area reservation window
	# Staff and service personnel are exempt from this rule.
	if not operator.is_staff and not operator.is_service_personnel and tool.requires_area_reservation():
		if not tool.requires_area_access.get_current_reservation_for_user(operator):
			abuse_email_address = get_customization('abuse_email_address')
			message = get_media_file_contents('unauthorized_tool_access_email.html')
			if abuse_email_address and message:
				dictionary = {
					'operator': operator,
					'tool': tool,
					'type': 'reservation',
				}
				rendered_message = Template(message).render(Context(dictionary))
				send_mail(subject="Area reservation requirement", content=rendered_message, from_email=abuse_email_address, to=[abuse_email_address], email_category=EmailCategory.ABUSE)
			return HttpResponseBadRequest("You must have a current reservation for the {} to operate this tool.".format(tool.requires_area_access.name))

	# Staff may only charge staff time for one user at a time.
	if staff_charge and operator.charging_staff_time():
		return HttpResponseBadRequest('You are already charging staff time. You must end the current staff charge before you being another.')

	# Staff may not bill staff time to themselves.
	if staff_charge and operator == user:
		return HttpResponseBadRequest('You cannot charge staff time to yourself.')

	# Users may only charge to projects they are members of.
	if project not in user.active_projects():
		return HttpResponseBadRequest('The designated user is not assigned to the selected project.')

	# The tool operator must not have a lock on usage
	if operator.training_required:
		return HttpResponseBadRequest(f"You are blocked from using all tools in the {facility_name}. Please complete the {facility_name} rules tutorial in order to use tools.")

	# Users may only use a tool when delayed logoff is not in effect. Staff and service personnel are exempt from this rule.
	if tool.delayed_logoff_in_progress() and not operator.is_staff and not operator.is_service_personnel:
		return HttpResponseBadRequest("Delayed tool logoff is in effect. You must wait for the delayed logoff to expire before you can use the tool.")

	# Users may not enable a tool during a scheduled outage. Staff and service personnel are exempt from this rule.
	if tool.scheduled_outage_in_progress() and not operator.is_staff and not operator.is_service_personnel:
		return HttpResponseBadRequest("A scheduled outage is in effect. You must wait for the outage to end before you can use the tool.")

	#Refuses all tool logins if user is logged in using an excluded project (i.e. one reserved for buddy system or observation)
	if not operator.is_staff:
		projects_to_exclude = []
		exclude=get_customization('exclude_from_usage')
		if exclude:
			projects_to_exclude = [int(s) for s in exclude.split() if s.isdigit()]
		try:
			if tool.requires_area_access:
				current_access = AreaAccessRecord.objects.filter(area=tool.requires_area_access, customer=operator, staff_charge=None, end=None)
				if current_access[0].project.id in projects_to_exclude:
					return HttpResponseBadRequest("You may not use tools while logged in with this project.")
		except:
			return HttpResponseBadRequest("There was a problem enabling this tool. Please see staff.")

	if tool.reservation_required and not operator.is_staff:
		td=timedelta(minutes=15)
		if not Reservation.objects.filter(start__lt=timezone.now()+td, end__gt=timezone.now(), cancelled=False, missed=False, shortened=False, user=operator, tool=tool).exists():
			return HttpResponseBadRequest("A reservation is required to enable this tool.")

	return HttpResponse()