Skip to content

Commit 5abb40f

Browse files
committed
fix: harden employee profile and project access
1 parent 6572dd9 commit 5abb40f

5 files changed

Lines changed: 57 additions & 30 deletions

File tree

time_tracking/time_tracking/doctype/time_tracking_profile/test_time_tracking_profile.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,14 @@ def test_profile_insert_creates_opening_ledgers(self):
109109
},
110110
)
111111
self.assertTrue(vacation_entry)
112+
113+
def test_employee_can_load_profile_ui_settings_without_settings_read_access(self):
114+
user = self._make_user("ui-settings")
115+
116+
frappe.set_user(user)
117+
settings = frappe.call(
118+
"time_tracking.time_tracking.doctype.time_tracking_profile.time_tracking_profile.get_profile_ui_settings"
119+
)
120+
121+
self.assertIn("track_target_adjustments", settings)
122+
self.assertIn("require_project_assignment", settings)

time_tracking/time_tracking/doctype/time_tracking_profile/time_tracking_profile.js

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,45 @@ function applyRateSettings(frm) {
1111
}
1212

1313
const DEFAULT_WEEKS_PER_MONTH = 52 / 12;
14-
const SETTINGS_DOCTYPE = "Time Tracking Settings";
15-
const TRACK_TARGET_ADJUSTMENTS_FIELD = "track_target_adjustments";
16-
const REQUIRE_PROJECT_ASSIGNMENT_FIELD = "require_project_assignment";
17-
18-
function getTrackTargetAdjustmentsSetting(frm) {
19-
return frappe.db
20-
.get_single_value(SETTINGS_DOCTYPE, TRACK_TARGET_ADJUSTMENTS_FIELD)
21-
.then((value) => {
22-
return Number(value) === 1;
23-
});
14+
let profileUiSettingsPromise = null;
15+
16+
function getProfileUiSettings() {
17+
if (!profileUiSettingsPromise) {
18+
profileUiSettingsPromise = frappe
19+
.call({
20+
method: "time_tracking.time_tracking.doctype.time_tracking_profile.time_tracking_profile.get_profile_ui_settings",
21+
})
22+
.then((response) => response.message || {})
23+
.catch(() => {
24+
profileUiSettingsPromise = null;
25+
return {
26+
track_target_adjustments: false,
27+
require_project_assignment: false,
28+
};
29+
});
30+
}
31+
32+
return profileUiSettingsPromise;
33+
}
34+
35+
function getTrackTargetAdjustmentsSetting() {
36+
return getProfileUiSettings().then((settings) => Boolean(settings.track_target_adjustments));
2437
}
2538

2639
function applyTargetAdjustmentVisibility(frm) {
2740
if (!frm.fields_dict.target_adjustments) {
2841
return;
2942
}
3043

31-
getTrackTargetAdjustmentsSetting(frm).then((enabled) => {
44+
getTrackTargetAdjustmentsSetting().then((enabled) => {
3245
frm.set_df_property("target_adjustments_section", "hidden", !enabled);
3346
frm.set_df_property("target_adjustments", "hidden", !enabled);
3447
frm.refresh_field("target_adjustments");
3548
});
3649
}
3750

3851
function getRequireProjectAssignmentSetting() {
39-
return frappe.db
40-
.get_single_value(SETTINGS_DOCTYPE, REQUIRE_PROJECT_ASSIGNMENT_FIELD)
41-
.then((value) => {
42-
return Number(value) === 1;
43-
});
52+
return getProfileUiSettings().then((settings) => Boolean(settings.require_project_assignment));
4453
}
4554

4655
function applyProjectAssignmentVisibility(frm) {
@@ -121,7 +130,7 @@ function openTargetSwitchDialog(frm, newPeriod) {
121130
const dialogTitle =
122131
newPeriod === "Monthly" ? __("Switch to Monthly Target") : __("Switch to Weekly Target");
123132

124-
getTrackTargetAdjustmentsSetting(frm).then((trackAdjustments) => {
133+
getTrackTargetAdjustmentsSetting().then((trackAdjustments) => {
125134
const fields = [
126135
{
127136
fieldtype: "HTML",
@@ -221,7 +230,7 @@ function openTargetAdjustmentDialog(frm, targetPeriod, labelSuffix, direction) {
221230
const actionLabel =
222231
direction > 0 ? __("Increase {0}", [labelSuffix]) : __("Decrease {0}", [labelSuffix]);
223232

224-
getTrackTargetAdjustmentsSetting(frm).then((trackAdjustments) => {
233+
getTrackTargetAdjustmentsSetting().then((trackAdjustments) => {
225234
const fields = [
226235
{
227236
fieldname: "hours",

time_tracking/time_tracking/doctype/time_tracking_profile/time_tracking_profile.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,14 @@ def _require_project_assignment_setting():
314314
return cint(frappe.db.get_single_value("Time Tracking Settings", "require_project_assignment") or 0)
315315

316316

317+
@frappe.whitelist()
318+
def get_profile_ui_settings():
319+
return {
320+
"track_target_adjustments": bool(_track_target_adjustments_enabled()),
321+
"require_project_assignment": bool(_require_project_assignment_setting()),
322+
}
323+
324+
317325
def get_permission_query_conditions(user):
318326
if not user:
319327
user = frappe.session.user

time_tracking/time_tracking/doctype/time_tracking_project/test_time_tracking_project.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from uuid import UUID
22

33
import frappe
4+
from frappe.permissions import has_permission
45
from frappe.tests.utils import FrappeTestCase
56
from frappe.utils import today
67

@@ -108,3 +109,13 @@ def test_unbillable_time_counts_hours_but_not_revenue(self):
108109
self.assertEqual(project.actual_hours, 2)
109110
self.assertEqual(project.actual_amount, 100)
110111
self.assertEqual(project.actual_pay_amount, 100)
112+
113+
def test_employee_has_no_direct_project_read_access(self):
114+
user = self._make_user("projectread")
115+
project = self._make_project(self._unique("Restricted Read"))
116+
117+
frappe.set_user(user)
118+
119+
self.assertFalse(has_permission("Time Tracking Project", "read", doc=project.name))
120+
with self.assertRaises(frappe.PermissionError):
121+
frappe.client.get("Time Tracking Project", project.name)

time_tracking/time_tracking/doctype/time_tracking_project/time_tracking_project.json

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -346,18 +346,6 @@
346346
"share": 1,
347347
"write": 1
348348
},
349-
{
350-
"read": 1,
351-
"role": "Employee"
352-
},
353-
{
354-
"read": 1,
355-
"role": "Desk User"
356-
},
357-
{
358-
"read": 1,
359-
"role": "Time Tracking Employee"
360-
},
361349
{
362350
"read": 1,
363351
"role": "Time Tracking Manager"

0 commit comments

Comments
 (0)